概述
在Kotlin中,允许对类进行扩展,不需要继承或使用 Decorator 模式,通过一种特殊形式的声明,来实现具体实现某一具体功能。扩展函数是静态解析的,并未对原类增添函数或者属性,也就是说对其本身没有丝毫影响。
扩展函数
扩展类的函数, 即 Extension Function , 可以在已有类中添加新的方法, 比继承更加简洁和优雅.
定义形式
扩展函数定义形式:
fun receiverType.functionName(params){
body
}
- receiverType:表示函数的接收者,也就是函数扩展的对象
- functionName:扩展函数的名称
- params:扩展函数的参数,可以为NULL
扩展函数并没有对原类做修改,而是为被扩展类的对象添加新的函数。
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' 指代 list 实例
this[index1] = this[index2]
this[index2] = tmp
}
fun main(args: Array<String>) {
val l = mutableListOf(1, 2, 3)
l.swap(0, 2) // 'swap()' 函数内的 'this' 将指向 'l' 的值
println(l.toString())
}
// Log
[3, 2, 1]
先对MutableList声明一个扩展函数swap,用来调换不同位置的值.在测试实例中,声明了一个MutableList实例,其值为1,2,3.然后调用该扩展函数swap(0, 2),在Log中,可以清晰的看出,位置0和2的值做了互换。在实际开发时,如果对一个类某一功能并不通用,只有某些特殊场景使用时,可以使用扩展函数,从而显得灵活多变。
注:
this关键字指代接收者对象(receiver object)(也就是调用扩展函数时, 在点号之前指定的对象实例).
扩展函数是静态解析的
之前说过,扩展函数是静态解析的,也就是,它并不是接收者类型的虚拟成员,意味着调用扩展函数时,调用扩展函数时,具体被调用的的是哪一个函数,由调用函数的的对象表达式来决定的,而不是动态的类型决定的。比如先声明了两个类Person和Teacher,其中teacher继承于Person,
在测试实例中,分别对Person和Teacher声明了扩展函数doFly(),又声明了fly()方法,其参数是一个Person对象,在其方法体内,调用了Person的扩展函数。现在测试实例中,创建了一个Teacher对象teacher,调用fly()方法并将teacher过去,实际上会调用哪个扩展函数呢?
open class Person {
}
class Teacher: Person() {
}
// 测试实例
fun Person.doFly() {
println("Person do fly")
}
fun Teacher.doFly() {
println("Teacher do fly")
}
fun fly(person: Person) {
person.doFly()
}
fun main(args: Array<String>) {
var teacher: Teacher = Teacher()
fly(teacher)
}
// Log
Person do fly
从Log打印中,可以清晰的看到的是,fly(teacher)调用的是Person的扩展函数。因为调用哪个函数, 仅仅是由参数person声明的类型决定, 这里参数person的类型为Person类.
成员函数和扩展函数
当成员函数和扩展函数想碰时,就像一个亲生的和私生的,当私生的碰到亲生的会怎么样呢?在一个类中,成员函数和扩展函数的函数名一致时,如果调用此函数,会调用成员函数还是扩展函数呢?不妨我们试一下
open class Person {
fun doFly() {
println("亲生的")
}
}
fun Person.doFly() {
println("后生的")
}
fun main(args: Array<String>) {
val person: Person = Person()
person.doFly()
}
// Log打印
亲生的
在Person中声明了函数doFly(),并对其声明了扩展函数doFly()。在Log打印中,可以清晰的看到,当person调用doFly()函数时,调用的是成员函数,而不是扩展函数,意味着,如果类中存在成员函数, 同时又在同一个类上定义了同名的扩展函数, 并且与调用时指定的参数匹配, 这种情况下,总是会优先使用成员函数。
接收者可为NULL
在扩展函数内, 可以通过this来接收者是否为NULL,这样,即使接收者为NULL,也可以调用扩展函数。例如
fun Person.doFly() {
if (null == this) {
println("null")
}
println("doFly")
}
扩展属性
扩展属性, 即 Extension Property , 即把某些函数添加为数据, 使用”=”, 直接设置或使用。
val List.lastIndex: Int
get() = size - 1
注:
由于扩展属性实际上不会向类添加新的成员, 因此无法让一个扩展属性拥有一个后端域变量. 所以,对于扩展属性不允许存在初始化器. 扩展属性的行为只能通过明确给定的取值方法与设值方法来定义,也就意味着扩展属性只能被声明为val而不能被声明为var.如果强制声明为var,即使进行了初始化,在运行也会报异常错误,提示该属性没有后端域变量。
伴随对象的扩展
在Kotlin-伴随对象中了解到,伴随对象通过“类名.”形式调用伴随对象。假如对伴随对象声明了扩展函数该怎么调用呢?其调用方式与伴随对象一样,都是通过用类名限定符来调用。当然,扩展属性也是这样的。
fun Person.Companion.doSwim() {
println("伴随对象的扩展函数")
}
val Person.Companion.no: Int
get() = 10
fun main(args: Array<String>) {
println("age:${Person.age}")
Person.doSwim()
}
将扩展定义为成员
在类的内部,可以为另外一个类定义扩展,在这个扩展中,有个多个隐含的接受者,其中扩展方法定义所在类的实例称为派发接受者,而扩展方法的目标类型的实例称为扩展接受者。
open class Person {
fun doFly() {
println("Person do fly")
}
}
class MyInfo {
fun doRun() {
println("MyInfo do run")
}
fun Person.doSwim() {
doFly()
doRun()
}
fun doSomething(person: Person) {
person.doSwim()
}
}
fun main(args: Array<String>) {
val myInfo: MyInfo = MyInfo()
val person: Person = Person()
myInfo.doSomething(person)
}
// Log
Person do fly
MyInfo do run
在MyInfo类内,创建了Person类的扩展。此时,MyInfo被成为派发接受者,而Person为扩展接受者。从上例中,可以清楚的看到,在扩展函数中,可以调用派发接收者的成员函数。假如在调用某一个函数,而该函数在派发接受者和扩展接受者均存在,此时扩展函数将会调用哪一个呢?写段代码测试下:
class MyInfo {
fun doRun() {
println("MyInfo do run")
}
fun doFly() {
println("MyInfo do fly")
}
fun Person.doSwim() {
doFly()
doRun()
}
fun doSomething(person: Person) {
person.doSwim()
}
}
// 测试实例
fun main(args: Array<String>) {
val myInfo: MyInfo = MyInfo()
val person: Person = Person()
myInfo.doSomething(person)
}
// Log
Person do fly
MyInfo do run
现在MyInfo类中,声明了一个函数doFly(),其函数名与Person中的doFly()一致。从Log打印中,我们可以清晰的看到,扩展函数实际上调用的是Person类中的doFly()函数,也就是,意味着,当派发接受者与扩展接受者的成员名称发生冲突时, 扩展接受者的成员将会被优先使用.如想同时调用Person和MyInfo中的doFly()函数或者是优先调用MyInfo中的doFly()函数,我们该怎么处理呢?在这里,我们不由想到了this@label,来指定this所属的范围,也就说我们可以这么做,this@MyInfo.doFly(),此时我们指定了this属于MyInfo类,从而达到了我们所想效果。
class MyInfo {
fun doRun() {
println("MyInfo do run")
}
fun doFly() {
println("MyInfo do fly")
}
fun Person.doSwim() {
doFly()
this@MyInfo.doFly()
doRun()
}
fun doSomething(person: Person) {
person.doSwim()
}
}
// 测试实例
fun main(args: Array<String>) {
val myInfo: MyInfo = MyInfo()
val person: Person = Person()
myInfo.doSomething(person)
}
// Log
Person do fly
MyInfo do fly
MyInfo do run
以成员的形式定义的扩展函数, 可以声明为 open , 而且可以在子类中覆盖. 也就是说, 在这类扩展函数的派
发过程中, 针对派发接受者是虚拟的(virtual), 但针对扩展接受者仍然是静态的。
open class D {
}
class D1 : D() {
}
open class C {
open fun D.foo() {
println("D.foo in C")
}
open fun D1.foo() {
println("D1.foo in C")
}
fun caller(d: D) {
d.foo() // 调用扩展函数
}
}
class C1 : C() {
override fun D.foo() {
println("D.foo in C1")
}
override fun D1.foo() {
println("D1.foo in C1")
}
}
C().caller(D()) // 打印结果为 "D.foo in C"
C1().caller(D()) // 打印结果为 "D.foo in C1" - 派发接受者的解析过程是虚拟的
C().caller(D1()) // 打印结果为 "D.foo in C" - 扩展接受者的解析过程是静态的