Kotlin官网:Classes and Objects-Extensions
Kotlin支持在一个类使用“扩展”增加功能,无需继承或者使用装饰模式之类的设计模式。
支持扩展函数和扩展属性。
声明扩展函数的格式为:被扩展类型.扩展函数名
下例为给MutableList类型增加swap函数
fun MutableList.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
扩展函数中的this关键字指被扩展类型对象。
使用扩展函数:
val l = mutableListOf(1, 2, 3)
l.swap(0, 2) // 'this' inside 'swap()' will hold the value of 'l'
扩展函数适用于任何MutableList类型,声明为泛型:
fun MutableList.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
泛型的说明参考3-8
扩展函数并未改变原类,仅仅是给这个类型增加了.调用函数的能力。
对扩展函数的调用是静态分配的,由函数声明的类型决定,而不是在运行时动态解析。
举个例子:
open class C
class D: C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
printFoo(D())
上例中打印结果为c,这是因为printFoo声明的参数类型为C,决定了使用C类型的扩展函数。
扩展函数名和类成员函数名相同时,使用类的成员函数
class C {
fun foo() { println("member") }
}
fun C.foo() { println("extension") }
c.foo()是打印结果为”member”,而不是”extension”.
当扩展函数和成员函数的函数签名不同时,两者互不干扰。
class C {
fun foo() { println("member") }
}
fun C.foo(i: Int) { println("extension") }
调用C().foo(1) ,打印结果”extension”
可以为可空类型增加扩展,在对象为空时也可以调用,在扩展函数中用this==null判断是否为空。
这是Kotlin允许调用空对象的toString的原理。
fun Any?.toString(): String {
if (this == null) return "null"
// after the null check, 'this' is autocast to a non-null type, so the toString() below
// resolves to the member function of the Any class
return toString()
}
和扩展函数类似,也是被扩展类型.属性名.
必须通过自定义访问器实现。
val <T> List<T>.lastIndex: Int
get() = size - 1
由于扩展并不是给原类型增加成员,所以不支持属性的后备字段,不支持初始化器。
扩展属性的行为是通过自定义访问器实现的。
如果类有伴生对象,可以给伴生对象扩展属性和函数
class MyClass {
companion object { } // will be called "Companion"
}
fun MyClass.Companion.foo() {
// ...
}
伴生对象的扩展成员的调用方法和普通成员相同,类名.扩展成员名
大多数情况下扩展定义在顶层,例如包中
package foo.bar
fun Baz.goo() { ... }
在定义的包外使用需要导入
package com.example.usage
import foo.bar.goo // 导入方法1:导入名为“goo”的扩展成员
import foo.bar.* // 导入方法2:导入foo.bar中的所有内容
fun usage(baz: Baz) {
baz.goo()
}
在类内部声明的其他类扩展,可以直接调用类中的成员,无需.声明。
声明扩展的类的实例叫做分发接收者
被扩展类的实例叫做扩展接收者
class D {
fun bar() { ... }
}
class C {
fun baz() { ... }
fun D.foo() {
bar() // calls D.bar
baz() // calls C.baz
}
fun caller(d: D) {
d.foo() // call the extension function
}
}
重名时被扩展的类的成员优先被调用,可以使用this关键字调用分发接受者的成员。
class C {
fun D.foo() {
toString() // calls D.toString()
this@C.toString() // calls C.toString()
}
}
声明为成员的扩展可以是open的,可以被子类重写。
对分发接收者而言是动态分配的。
对扩展接受者依然是静态分配的。
open class D {
}
class D1 : D() {
}
open class C {
open fun D.foo() {
println("D.foo in C")
}
open fun D1.foo() {
println("D1.foo in C")
}
fun caller(d: D) {
d.foo() // call the extension function
}
}
class C1 : C() {
override fun D.foo() {
println("D.foo in C1")
}
override fun D1.foo() {
println("D1.foo in C1")
}
}
C().caller(D()) // prints "D.foo in C"
C1().caller(D()) // prints "D.foo in C1" - dispatch receiver is resolved virtually
C().caller(D1()) // prints "D.foo in C" - extension receiver is resolved statically
上例中,对于C和C1是动态分配的,C1中D.foo被重写,caller打印出的是C1重写的D.foo。而对于caller函数中的d参数,由于是静态分配的,无论传入D的实例还是D1的实例,都会调用函数签名中D类的扩展函数D.foo,而不会去调用D1.foo。
为某些类添加工具方法。
例如Java中的各种工具类,以java.utilCollections为例,使用时需要用Collections.方法名(集合实例,参数)这样调用。
Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list));
// import static
swap(list, binarySearch(list, max(otherList)), max(list));
直接写成实例的方法最好
list.swap(list.binarySearch(otherList.max()), list.max());
但是又不想在List中添加所有可能使用的方法,这时可以使用扩展。xu