提示:本文为作者阅读Kotlin中文站学习笔记,建议读者移步Kotlin中文站完整学习。
Kotlin中,我们可以扩展一个类的函数和属性,而无需继承这个类或使用像装饰者这样的设计模式。
声明一个扩展函数,我们需要一个接受者类型也就是被扩展的类型作为前缀,例如下面代码给Context扩展一个toast函数
fun Context.toast(message: String, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, duration).show()//this对应接收者类型实例(即Context实例)
}
这样,我们就可以对任何Context实例调用toast方法,假如我们在Activity里要弹出一个土司就可以这样写:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
this.toast("Hello Kotlin!")//任何一个Context实例都可以调用扩展函数toast
}
}
值得注意的是,扩展并没有真正修改所扩展的类。通过定义一个扩展,并没有在类中加入一个新的成员,只是我们可以通过这个类的实例用点表达式去访问这个扩展的函数或属性。
扩展函数是静态分发的,即他们不是根据接收者类型的虚方法。这意味着调用的扩展函数是由调用时所在的表达式类型决定的,而不是运行是表达式的求值结果决定的。
open class X
class Y:X()
class Z{
fun X.foo(){
print("foo in X")
}
fun Y.foo(){
print("foo in Y")
}
fun printFoo(x:X){
x.foo()//扩展函数静态分发,只与函数调用时的接收者类型有关,而与实际运行时的接收者类型无关,
// 所以这里始终调用的是X类型的扩展方法,而不管实际运行时传入的是X类型实例,还是Y类型实例
}
printFoo(Y())//foo in X
}
当一个类的成员函数和扩展函数,具有相同的接收者类型,相同的名称,相同的参数时,这时总是取成员函数。
class A{
//成员函数
fun foo(){
print("member foo for A")
}
}
class B{
//A类型的扩展函数
fun A.foo(){
print("extension foo for A")
}
fun A.foo(arg1:String){
print("extension with arg1 for A")
}
fun printFoo(){
A().foo()//member foo for A
//接收者类型相同,函数名相同,接受参数相同,总是取成员函数
A().foo("extension with arg1 for A")//extension with arg1 for A
}
}
我们可以为可空接收者类型定义扩展。这样的扩展在对象变量上调用,即使其值为null,我们可以在扩展函数内部做判空操作:
fun Any?.toString(){
if (this==null) return;
this.toString()
}
和扩展函数类似,我们也可以为一个类扩展属性,因为扩展属性并没有实际的加入到类的成员中,所以扩展属性没有幕后字段,不能初始化,必须由我们自定义访问器。
var Array.lastIndex:Int//扩展属性,显式定义访问器
get() = this.size-1
set(value) {
this.lastIndex=value
}
fun _getLastIndex(){
var arr:Array
arr=arrayOf(1,2,3)
arr.lastIndex
}
我们可以为类的伴生对象定义扩展函数,并且可以只通过类名访问扩展函数
class C{
companion object{}//伴生对象
}
class D{
fun C.Companion.foo(){//为C类的伴生对象定义扩展
print("foo in C.Companion")
}
fun printFoo(){
C.foo()//foo in C.Companion
//伴生对象的扩展可以直接通过类名访问
}
}
在一个类内部你可以为另一个类声明扩展。在这样的扩展内部,有多个 隐式接收者 —— 其中的对象成员可以无需通过限定符访问。扩展声明所在的类的实例称为 分发接收者,扩展方法调用所在的接收者类型的实例称为 扩展接收者 。对于分发接收者和扩展接收者的成员名字冲突的情况,扩展接收者优先。要引用分发接收者的成员你可以使用 限定的 this 语法。
class E{
fun fun0(){
print("fun0 in E")
}
}
class F{
fun fun0(){
print("fun0 in F")
}
fun E.foo(){
fun0()//调用E中的fun0方法
[email protected]()//调用F中的fun0方法
}
}
扩展函数可以在子类中覆盖,这种情况下,函数的分发对于分发接收者是虚拟的,对于扩展接收者是静态的。
open class H
class I: H()
open class G{
open fun H.foo(){
print("H.foo in G")
}
open fun I.foo(){
print("I.foo in G")
}
fun printFoo(h:H){
h.foo()
}
}
class K: G(){
override fun H.foo() {
print("H.foo in K")
}
override fun I.foo() {
print("I.foo in K")
}
}
fun printFoo(){
G().printFoo(H())//H.foo in G
G().printFoo(I())//H.foo in G
K().printFoo(H())//H.foo in K
K().printFoo(I())//H.foo in K
}
扩展函数永远与函数调用时的类型相同,成员函数则由运行时的具体类型决定。