3.8 函数扩展和属性扩展(Extensions)

Kotlin 支持 扩展函数 和 扩展属性。其能够扩展一个类的新功能而无需继承该类或使用像装饰者这样的设计模式等。

大多数时候我们在顶层定义扩展,即直接在包里:

1. package com.easy.kotlin

3. val <T> List<T>.lastIndex: Int get() = size - 1

5. fun String.notEmpty(): Boolean {
6. return !this.isEmpty()
7. }

这样我们就可以在整个包里使用这些扩展。

要使用其他包的扩展,我们需要在调用方导入它:

1. package com.example.usage

3. import foo.bar.goo // 导入所有名为“goo”的扩展
4. // 或者
5. import foo.bar.*   // 从“foo.bar”导入一切

7. fun usage(baz: Baz) {
8. baz.goo()
9. }

3.8.1 扩展函数

声明一个扩展函数,我们需要用被扩展的类型来作为前缀。

比如说,我们不喜欢类似下面的双重否定式的逻辑判断(绕脑子):

1. >>> !"123".isEmpty()
2. true

我们就可以为String类型扩展一个notEmpty()函数:

1. >>> fun String.notEmpty():Boolean{
2. ... return !this.isEmpty()
3. ... }

5. >>> "".notEmpty()
6. false

8. >>> "123".notEmpty()
9. true

下面代码为 MutableList<Int> 添加一个swap 函数:

1. fun MutableList<Int>.swap(index1: Int, index2: Int) {
2. val tmp = this[index1] // this对应该列表
3. this[index1] = this[index2]
4. this[index2] = tmp
5. }

这个 this 关键字在扩展函数内部对应到接收者对象(传过来的在点.符号前的对象) 现在,我们对任意 MutableList<Int> 调用该函数了。

当然,这个函数对任何 MutableList<T> 起作用,我们可以泛化它:

1. fun <T> MutableList<T>.mswap(index1: Int, index2: Int) {
2. val tmp = this[index1] // “this”对应该列表
3. this[index1] = this[index2]
4. this[index2] = tmp
5. }

为了在接收者类型表达式中使用泛型,我们要在函数名前声明泛型参数。

完整代码示例

1. package com.easy.kotlin

3. val <T> List<T>.lastIndex: Int get() = size - 1

5. fun String.notEmpty(): Boolean {
6. return !this.isEmpty()
7. }

9. fun MutableList<Int>.swap(index1: Int, index2: Int) {
10. val tmp = this[index1] // this对应该列表m
11. this[index1] = this[index2]
12. this[index2] = tmp
13. }

15. fun <T> MutableList<T>.mswap(index1: Int, index2: Int) {
16. val tmp = this[index1] // “this”对应该列表
17. this[index1] = this[index2]
18. this[index2] = tmp
19. }

21. class ExtensionsDemo {

23. fun useExtensions() {
24. val a = "abc"
25. println(a.notEmpty())//true

27. val mList = mutableListOf<Int>(1, 2, 3, 4, 5)
28. println("Before Swap:")
29. println(mList)//[1, 2, 3, 4, 5]
30. mList.swap(0, mList.size - 1)
31. println("After Swap:")
32. println(mList)//[5, 2, 3, 4, 1]

34. val mmList = mutableListOf<String>("a12", "b34", "c56", "d78")
35. println("Before Swap:")
36. println(mmList)//[a12, b34, c56, d78]
37. mmList.mswap(1, 2)
38. println("After Swap:")
39. println(mmList)//[a12, c56, b34, d78]

41. val mmmList = mutableListOf<Int>(100, 200, 300, 400, 500)
42. println("Before Swap:")
43. println(mmmList)
44. mmmList.mswap(0, mmmList.lastIndex)
45. println("After Swap:")
46. println(mmmList)
47. }

49. class Inner {
50. fun useExtensions() {
51. val mmmList = mutableListOf<Int>(100, 200, 300, 400, 500)
52. println(mmmList.lastIndex)
53. }
54. }
55. }

测试代码

1. package com.easy.kotlin

3. import org.junit.Test
4. import org.junit.runner.RunWith
5. import org.junit.runners.JUnit4

7. @RunWith(JUnit4::class)
8. class ExtensionsDemoTest {
9. @Test fun testExtensionsDemo() {
10. val demo = ExtensionsDemo()
11. demo.useExtensions()
12. }
13. }

扩展不是真正的修改他们所扩展的类。我们定义一个扩展,其实并没有在一个类中插入新函数,仅仅是通过该类型的变量,用点.表达式去调用这个新函数。

3.8.2 扩展属性

和函数类似,Kotlin 支持扩展属性:

1. val <T> List<T>.lastIndex: Int
2. get() = size - 1

注意:由于扩展没有实际的将成员插入类中,因此对扩展的属性来说,它的行为只能由显式提供的 getters/setters 定义。

代码示例:

1. package com.easy.kotlin

3. val <T> List<T>.lastIndex: Int get() = size - 1

我们可以直接使用包com.easy.kotlin中扩展的属性lastIndex :

Kotlin极简教程