扩展

QuibblerQuibbler 2020-06-14 约 11 分钟 1809 次阅读

扩展

Kotlin可以对现有的类进行扩展,添加原本没有定义在类中的成员。不仅可以对Kotlin代码进行扩展,只要能编译为Java类,都可以为这个类添加任意扩展。Kotlin巧妙的用了扩展的特性为Java标准类库扩展了很多API方法,方便直接调用。

1、扩展函数

1.1、定义扩展函数

扩展函数的语法如下:

fun extensionClass.functionName(params){

    body                                                    

}                                                                    

例:

class Base {
}

//对Base类扩展,添加who()函数
fun Base.who() {
}

Base类型的对象就能够调用who()方法:

var base = Base()
    base.who()

1.2、扩展函数中的this

定义的扩展函数就当成类中的成员方法一样使用,可以使用this,表示当前的对象实例。注意:扩展函数不会破坏原有的封装性,和类内部的方法不同,扩展函数不能访问private或者protected修饰的成员。修饰符详见类和对象第一节。

class Base {
    var num: Int = 0
}

//通过this关键字访问类中的num成员变量
fun Base.who() {
    this.num = 1
}

1.3、扩展函数名和类原有类一样

如果新增的扩展函数和原来定义的在被扩展类中的函数一模一样,会不会“覆盖”原来的函数呢?

class Base {
    fun who() {
        println("origin")
    }
}

//新增扩展的函数和原有类中的方法一样
fun Base.who() {
    println("extension")
}

实际会优先调用类中定义的成员方法

1.4、扩展函数不具备多态

扩展函数是静态解析的,并不是接收者类型的虚拟成员,在调用扩展函数时,具体被调用的的是哪一个函数,由调用函数的的对象表达式的静态类型来决定的,而不是运行时动态的类型决定的。

如果父类和派生类都定义了同名的扩展函数:

open class Base {
}
fun Base.who() {
    println("Base.who")
}

class Sub : Base() {
}
fun Sub.who() {
    println("Sub.who")
}

通过抽象的父类类型访问派生类对象,调用扩展方法,调用的是父类的扩展函数还是派生类的扩展函数呢?

var obj: Base = Sub()
    obj.who()

答案是:调用的是声明对象时的Base类的扩展方法。扩展函数不存在重写,因为Kotlin会把它们当做静态函数对待。

2、扩展变量

除了给类添加扩展函数,还可以新增成员变量。控制的属性有一些限制:

①扩展属性只能被声明为val

②扩展属性只能定义在类或者Kotlin文件中,不允许定义在函数中

③扩展属性不允许被初始化,通过get方法间接提供值

例:

open class Base {
}

val Base.num: Int
    get() = 1

3、扩展Any超类

觉得扩展非常强悍,竟然还能扩展超类Any:

fun Any?.toString(): String {
    if (this == null) return "null"
    return this.toString()
}

这样的话岂不是能规避一些潜在的空指针带来的问题,

var any: Any? = null
    println(any.toString())

当调用一个为空的Any对象,不会异常,而是输出“null”。

4、扩展作用域

扩展定义在文件中

package main

open class Base {
}

fun Base.who(){
    
}

一个类的扩展函数也可以定义在其它包、其它文件中

package main.grammar.other

import main.Base

fun Base.other() {
}

如果想使用上面定义的扩展,需要明确导入定义的扩展方法,本质上可以将扩展函数理解为Java中的静态方法:

import main.grammar.other.other

5、在类内部声明扩展

在类的内部可以扩展另一个类。只能在类内部使用定义的扩展,外部无法访问到。

5.1、分发接受者&扩展接受者

扩展方法定义所在类的实例称为分发接受者,而扩展方法的目标类型的实例称为扩展接受者。

例:

class Base {
}

class Extension {
    fun Base.who() {
    }
}

Extension是扩展的分发接受者,Base则成为who()扩展接受者。

5.2、扩展方法调用类中的方法

在类中定义的其它类的扩展方法如果调用类中的方法,默认调用的是扩展接受者类中的方法。

例:

class Base {
    fun my() {
    }
}

class Extension {
    fun my() {
    }
    
    fun Base.who() {
        my() //调用的是Base中的my()方法,而不是当前类Extension的my()方法
    }
}

如果需要调用当前定义的类中的方法,需要用this@Class明确指定调用哪一个类。

例:

class Base {
    fun my() {
    }
}

class Extension {
    fun my() {
    }
    
    fun Base.who() {
        this@Extension.my() //
    }
}

5.3、扩展重写

在父类中对某个类进行了扩展。

例:

class Extension {
}

添加一个open修饰的扩展方法

open class Base {
    open fun Extension.who() {
        println("Base Extension.who()")
    }
    fun caller(ext: Extension) {
        ext.who()
    }
}

5.3.1、对于分发接受者是虚拟的(virtual)

在子类中重写父类对于Extension类的扩展方法

class Sub : Base() {
    override fun Extension.who() {
        println("Sub Extension.who")
    }
}

这个时候是动态绑定的,即使通过父类的指针引用访问派生类对象实例,调用的是重写过的扩展方法

var a:Base = Sub()
    a.caller(Extension())
    //输出"Sub Extension.who"

5.3.2、对于扩展接受者是静态

定义一个类

open class Extension {
}

以及继承这个类的另一个子类

class OtherExtension : Extension() {
}

在类中可以分别对两个类Extension和其子类OtherExtension添加扩展:

open class Base {
    fun Extension.who() {
        println("Extension.who()")
    }
    fun OtherExtension.who() {
        println("OtherExtension.who()")
    }
    
    fun caller(obj: Extension) {
        obj.who()
    }
}

Extension和其子类OtherExtension就是扩展接受者,可以看见caller()调用方法传入的是基类,当实际传入的是子类对象实例,调用的是哪一个扩展方法呢?

Base().caller(OtherExtension())

答案是调用子类的扩展方法,输出:"Extension.who()"

除非再加一个使用派生类当做参数的方法调用派生类中添加的扩展方法,因为对于扩展函数不具备多态,是静态绑定的方法。

open class Base {
    fun Extension.who() {
        println("Extension.who()")
    }
    fun OtherExtension.who() {
        println("OtherExtension.who()")
    }
    
    fun caller(obj: Extension) {
        obj.who()
    }
    
    fun caller(obj: OtherExtension) {
        obj.who()
    }
}

学习资料:

        Kotlin 扩展

相关推荐

Parcelize:Parcelable 实现生成器
Kotlin

Parcelize:Parcelable 实现生成器

Parcelize:Parcelable 实现生成器Parcelable是安卓开发中常用的序列化方式,之前分享过一个Parcelable序列化插件,方便开发者快速生成代码,提高开发效率。1、Parcelize插件Kotlin官方也推出一个Parcelable自动生成的Gradle插件:Parcelize。1.1、引入插件在项目的build.gradle中添加:id("kotlin-parceliz

2.6k
Kotlin的继承
Kotlin

Kotlin的继承

Kotlin的继承1、超类:Any类似于Java中的Object类,Kotlin也有一个叫Any的超类,是所有类的父类,所有类都默认继承自该类。超类的定义如下,其中定义了三个函数:2、open注意上面的Any类用open修饰,表示该类可以被继承。Kotlin中的类默认都是final的,无法被继承:如果想让类可以被继承,就需要用在定义类的地方用open关键字标识该类可以被继承。3、构造器继承中涉及到

2.1k
 Kotlin基本语法
Kotlin

Kotlin基本语法

Kotlin基本语法了解Kotlin语言的基本语法。1、包kotlin文件以.kt结尾,编译文件还是如此只不过在文件名后面加上Kt后缀。这一点和Java不同,Java源文件.java,编译后为.class文件。1.1、包定义每个kotlin文件的第一行就用关键字package定义该文件所在的包。例:1.2、导包用到其它kotlin文件需要使用关键字import导入,这一点和Java一样。kotli

2.1k