扩展函数
Kotlin的扩展函数是一种特殊的函数,它可以为一个已有的类添加新的功能,而不需要修改或继承这个类。
写法
定义扩展函数
扩展函数的定义是在函数名前加上要扩展的类名和一个点号,例如:
fun String.lastChar(): Char {
return this[this.length - 1]
}
这个函数为String类添加了一个lastChar()方法,它可以返回字符串的最后一个字符。注意,扩展函数并不会真的修改原来的类,它只是一种语法糖,让我们可以用点号调用这个函数,就像调用类的成员方法一样。
在何处定义扩展函数?
要为String类添加一个lastChar()方法,我们可以在任何地方定义一个扩展函数,只要它在我们需要使用它的地方是可见的。例如,我们可以在一个单独的文件中定义这个函数,然后在其他文件中导入它:
// Extensions.kt
package com.example
fun String.lastChar(): Char {
return this[this.length - 1]
}
// Main.kt
package com.example
import com.example.lastChar // 导入扩展函数
fun main() {
val s = "Hello"
println(s.lastChar()) // o
}
或者,我们可以在一个类或对象中定义这个函数,然后在这个类或对象的作用域内使用它:
class StringUtil {
companion object {
fun String.lastChar(): Char {
return this[this.length - 1]
}
}
}
fun main() {
val s = "Hello"
println(StringUtil.s.lastChar()) // o
}
注意,如果我们在一个类或对象中定义扩展函数,我们需要使用限定的this来访问receiver对象,即在this前面加上类或对象的名字。例如,StringUtil.this.lastChar()。
性能优化:如果想使用inline内联来进行性能优化,那么在定义高阶函数的地方加上inline就对了。
调用扩展函数
Kotlin中的扩展函数有一个叫做receiver的东西,它指的是被扩展的类的类型。在上面的例子中,String就是receiver类型,它表示这个函数是为String类扩展的。在扩展函数内部,我们可以使用this关键字来访问receiver对象,也就是调用这个函数的对象。例如:
val s = "Hello"
println(s.lastChar()) // 输出o
这里,s就是receiver对象,它是一个String类型的变量。我们可以用this来代替s,如this.lastChar(),但通常可以省略this。
好处
扩展函数的好处是可以让我们为任何类添加自定义的功能,而不需要修改源码或创建子类。这样可以让我们的代码更简洁、更易读、更灵活。
限制
Kotlin的扩展函数有以下几个限制:
- 扩展函数不能访问被扩展类的私有或受保护的成员,因为它们并不是真正的类成员,只是一种外部函数。
- 扩展函数是静态解析的,也就是说,它们不是多态的。调用哪个扩展函数只取决于变量的声明类型,而不是运行时的实际类型。
- 扩展函数不能被重写,也就是说,如果一个子类定义了一个和父类相同的扩展函数,它并不会覆盖父类的扩展函数。
- 扩展函数不能和类成员函数同名,否则会造成歧义。如果一个类有一个成员函数和一个扩展函数,它们有相同的接收者类型、相同的名字,并且适用于给定的参数,那么总是优先调用成员函数。
扩展属性
和Kotlin的扩展函数相似,Kotlin的扩展属性是一种特殊的属性,它可以为一个已有的类添加新的属性,而不需要修改或继承这个类。
写法
定义扩展属性
扩展属性的定义是在属性名前加上receiver类型和一个点号,例如:
val String.lastChar: Char
get() = this[this.length - 1]
这个属性为String类添加了一个lastChar属性,它可以返回字符串的最后一个字符。这里,String就是receiver类型,它表示这个属性是为String类扩展的。
调用扩展属性
在扩展属性内部,我们可以使用this关键字来访问receiver对象,也就是调用这个属性的对象。例如:
val s = "Hello"
println(s.lastChar) // o
这里,s就是receiver对象,它是一个String类型的变量。我们可以用this来代替s,如this.lastChar,但通常可以省略this。
扩展属性和扩展函数的相似之处
扩展属性和扩展函数有以下几个相似之处:
- 它们都需要指定receiver类型和receiver对象。
- 它们都不能访问被扩展类的私有或受保护的成员。
- 它们都是静态解析的,也就是说,它们不是多态的。
- 它们都不能和类成员同名,否则会造成歧义。
扩展属性和扩展函数的不同之处
扩展属性和扩展函数有以下几个不同之处:
- 扩展属性不能有初始化器,也就是说,它们不能有=后面的表达式。
- 扩展属性必须有显示定义的getter函数,如果是可变属性,还必须有setter函数。
- 扩展属性不能有幕后字段(backing field),也就是说,它们不能使用field关键字来访问存储值。