- 开发环境:IntelliJ IEDA
- 官方文档中文版:https://www.kotlincn.net/docs/reference/extensions.html
Kotlin同C# 和Gosu类似,能够扩展一个类的新功能而无需继承该类或使用像装饰者这样任何类型的设计模式。这通过叫做扩展的特殊声明完成。Kotlin支持扩展函数和扩展属性。
1、扩展函数
(1)定义
声明一个扩展函数,我们需要用一个接收者类型也就是被扩展的类型来作为它的前缀。例如:为MutableList<Int>
添加一个swap
函数:
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val temp = this[index1]
this[index1] = this[index2]
this[index2] = temp
}
fun main(args: Array<String>) {
val l = mutableListOf(1, 2, 3)
l.swap(0, 1)
print(l)
}
由代码可以看出,这个扩展函数的主要功能就是将对应位置的对象进行替换。
这个this
关键字在扩展函数内部对应到接收者对象,即传过来的在点符号前的对象。
同样,这个函数对任何MutableList<T>
起作用,所以可以泛化它:
fun <T> MutableList<T>.swap2(index1: Int, index2: Int) {
val temp = this[index1]
this[index1] = this[index2]
this[index2] = temp
}
fun main(args: Array<String>) {
val s = mutableListOf("a", "b", "c")
s.swap2(0, 3)
println(s)
}
(2)静态解析
扩展不能真正的修改它们所扩展的类。通过定义一个扩展,并没有在一个类中插入新成员,仅仅是通过该类型的变量用点表达式去调用这个新函数。
也就是说扩展函数是静态分发的,即它们不是根据接收者类型的虚方法。这意味着调用的扩展函数是由函数调用所在的表达式的类型来决定的,而不是表达式运行时求值结果决定的。例如:
open class C
class D : C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
fun main(args: Array<String>) {
printFoo(D())
}
最后结果输出为“c”,因为调用的扩展函数只取决与参数c
的声明类型,该类型c
类。
如果一个类定义有一个成员函数和一个扩展函数,而这两个函数又有相同的接收者类型、相同的名字并且都适用给定的参数,这种情况总是取成员函数。即成员函数优先级大于扩展函数。例如:
class XX {
fun foo() {
println("member")
}
}
fun XX.foo() {
println("extension")
}
fun main(args: Array<String>) {
println(XX().foo())
}
调用XX().foo()
时输出结果为“member”,而不是“extension”。
如果扩展函数重载同样名字但不同签名成员函数也完全可以:
fun XX.foo(i: Int) {
println("i----->" + i)
}
fun main(args: Array<String>) {
println(XX().foo(3))
}
这时输出结果为:i----->3
(3)可空接收者
可以为可空的接收者类型定义扩展。这样的扩展可以在对象变量上调用,即使其值为null,并且可以函数体内检测this == null
,这能让你在没有检测null的时候调用Kotlin中的toString():检测发生在扩展函数的内部。
fun Any?.toString(): String {
if (this == null) return "null"
return this.toString()
}
这样当数值为null
的时候,会在内部进行判断,并返回null值,而不是抛出异常。
2、扩展属性
和函数类似,Kotlin支持扩展属性:
val <T> List<T>.lastIndex: Int
get() = size - 1
fun main(args: Array<String>) {
val ll = listOf(1, 2, 3, 4, 5)
println(ll.lastIndex)
}
另外,扩展属性不能有初始化器,它们的行为只能由显式提供的getters/setters定义。例如:
class Foo
val Foo.baz = 2//编译错误
val Foo.bar
get() = 3
fun main(args: Array<String>) {
println(Foo().bar)
}
3、伴生对象的扩展
如果一个类定义有一个伴生对象,也可以为伴生对象定义扩展函数和属性:
class MyClass {
companion object {}
}
fun MyClass.Companion.foo() {
print("11111111111")
}
fun main(args: Array<String>) {
//只需通过类名作为限定符去调用它们
MyClass.foo()
}
4、作用域
(1)扩展在包中
大多数时候我们在顶层定义扩展,即直接在包里:
package foo.bar
fun Baz.goo() {
//代码块
}
(2)其它包调用
要使用所定义包之外的一个扩展,我们需要在调用方导入它:
package com.example.usage
// 以名字 "goo" 导入所有扩展
import foo.bar.goo
// 或者 从 "foo.bar" 导入一切
import foo.bar.*
fun usage(baz: Baz) {
baz.goo()
)
5、扩展声明为成员
在一个类内部可以为另一类声明扩展。扩展声明所在的类称为分发接收者,扩展方法调用所在类称为扩展接收者。对于分发接收者和扩展接收者的成员名字冲突的情况,扩展接受者有限。要引用分发接收者的成员可以使用限定的this
语法。
//扩展接收者
class DD {
fun bar() {
println("bar")
}
}
//分发接收者
class CC {
fun baz() {
println("baz")
}
fun DD.foo() {
bar()
baz()
toString()//调用DD.toString()
this@CC.toString()//调用CC.toString()
}
fun caller(dd: DD) {
dd.foo()
}
}
声明为成员的扩展可以声明为open
并在子类中覆盖。这意味这这些函数的分发对于分发接受者类型是虚拟的,但对于扩展接收者类型是静态的。
open class Z {
}
class Z1 : Z() {
}
open class X {
open fun Z.foo() {
println("Z.foo in X")
}
open fun Z1.foo() {
println("Z1.foo in X")
}
fun caller(z: Z) {
z.foo()//调用扩展函数
}
}
class X1 : X() {
override fun Z.foo() {
println("Z.foo in X1")
}
override fun Z1.foo() {
println("Z1.foo in X1")
}
}
fun main(args: Array<String>) {
X().caller(Z())//输出Z.foo in X
X1().caller(Z())//输出Z.foo in X1 --分发接收者虚拟解析
X().caller(Z1())//输出Z.foo in X -- 扩展接受者静态解析
}