一、扩展函数
Kotlin中可以对一个类或接口扩展新功能而无需继承或类似Decorator对设计模式,例如我们希望对一些三方库中的类或接口添加新方法,调用方式可以与原始方法一致。
定义时用适用被扩展类型为前缀加扩展的方法名即可,函数内部this
指扩展类
1
2
3
4
5
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
也可以直接使用泛型,将对任何MutableList<T>
类型生效
1
2
3
4
5
6
7
8
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
val list = mutableListOf(1, 2, 3)
list.swap(0, 2) // 'this' inside 'swap()' will hold the value of 'list'
通常还可以定义为可空接收,内部通过this == null
进行校验
1
2
3
4
5
6
fun Any?.toString(): String {
if (this == null) return "null"
// After the null check, 'this' is autocast to a non-nullable type, so the toString() below
// resolves to the member function of the Any class
return toString()
}
二、扩展属性
像扩展函数一样,Kotlin同样支持扩展属性,扩展函数没有backing field 不允许使用设定初始值,只能通过显式的getter/setter来实现。
1
2
val <T> List<T>.lastIndex: Int
get() = size - 1
下面这样是错误的,不能知道设定初始值
1
val House.number = 1 // 错误:扩展属性不能有初始化器
三、伴生对象的扩展
伴生对象也可以直接定义扩展函数或属性,调用是与伴生对象的常规成员一样。
1
2
3
4
5
6
7
8
9
class MyClass {
companion object { } // 将被称为 "Companion"
}
fun MyClass.Companion.printCompanion() { println("companion") }
fun main() {
MyClass.printCompanion()
}
四、声明为类成员
可以在另一个类中声明当前类的扩展,仅在声明的当前作用域内有效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Host(val hostname: String) {
fun printHostname() { print(hostname) }
}
class Connection(val host: Host, val port: Int) {
fun printPort() { print(port) }
fun Host.printConnectionString() {
printHostname() // calls Host.printHostname()
print(":")
printPort() // calls Connection.printPort()
}
fun connect() {
/*...*/
host.printConnectionString() // calls the extension function
}
}
fun main() {
Connection(Host("kotl.in"), 443).connect()
//Host("kotl.in").printConnectionString() // error, the extension function is unavailable outside Connection
}
如果扩展内部存在调用冲突则使用this
关键字进行限定
1
2
3
4
5
6
class Connection {
fun Host.getConnectionString() {
toString() // calls Host.toString()
this@Connection.toString() // calls Connection.toString()
}
}
声明为成员扩展的可用open
关键字进行修饰,子类中可以进行重写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
open class Base { }
class Derived : Base() { }
open class BaseCaller {
open fun Base.printFunctionInfo() {
println("Base extension function in BaseCaller")
}
open fun Derived.printFunctionInfo() {
println("Derived extension function in BaseCaller")
}
fun call(b: Base) {
b.printFunctionInfo() // call the extension function
}
}
class DerivedCaller: BaseCaller() {
override fun Base.printFunctionInfo() {
println("Base extension function in DerivedCaller")
}
override fun Derived.printFunctionInfo() {
println("Derived extension function in DerivedCaller")
}
}
fun main() {
BaseCaller().call(Base()) // "Base extension function in BaseCaller"
DerivedCaller().call(Base()) // "Base extension function in DerivedCaller" - dispatch receiver is resolved virtually
DerivedCaller().call(Derived()) // "Base extension function in DerivedCaller" - extension receiver is resolved statically
}
五、总结
扩展函数最好定义为可空接收者类型,避免空对象调用时发生
NullPointerException
同时引入多个库时可能存在命名冲突,可以在导入时进行重命名
1
2
import com.library1.someFunction as lib1Function
import com.library2.someFunction as lib2Function
- 不要滥用扩展函数,滥用它们可能导致代码可读性下降。只有在逻辑上与类相关的操作才应该使用扩展函数。
- Kotlin 支持扩展属性,但它们不能有初始化器。这意味着你不能为扩展属性提供初始值,而只能在扩展的类中实现 getter
- 扩展函数和扩展属性不会真正地修改类,它们只是在调用时看起来像类的一部分。因此,扩展函数和属性不能访问类的私有成员
- 如果你的扩展函数与类自带的函数名相同,调用时会优先使用类自带的函数。因此,确保扩展函数的名字足够清晰,以避免与现有函数发生冲突。
- 扩展函数并不会真正地在被扩展的类中插入新的方法,而是通过静态方法调用实现。这意味着在性能要求高的情况下,可能需要谨慎使用扩展函数。