您的位置:首页 > 其它

Kotlin学习笔记3-5 类和对象-扩展

2018-01-23 14:48 561 查看

扩展

Kotlin官网:Classes and Objects-Extensions

Kotlin支持在一个类使用“扩展”增加功能,无需继承或者使用装饰模式之类的设计模式。

支持扩展函数和扩展属性。

扩展函数

声明扩展函数的格式为:被扩展类型.扩展函数名

下例为给MutableList类型增加swap函数

fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}


扩展函数中的this关键字指被扩展类型对象。

使用扩展函数:

val l = mutableListOf(1, 2, 3)
l.swap(0, 2) // 'this' inside 'swap()' will hold the value of 'l'


扩展函数适用于任何MutableList类型,声明为泛型:

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
}


泛型的说明参考3-8

扩展是静态解析

扩展函数并未改变原类,仅仅是给这个类型增加了.调用函数的能力。

对扩展函数的调用是静态分配的,由函数声明的类型决定,而不是在运行时动态解析。

父类和子类有同样的扩展函数

举个例子:

open class C

class D: C()

fun C.foo() = "c"

fun D.foo() = "d"

fun printFoo(c: C) {
println(c.foo())
}

printFoo(D())


上例中打印结果为c,这是因为printFoo声明的参数类型为C,决定了使用C类型的扩展函数。

扩展函数和类的成员函数相同

扩展函数名和类成员函数名相同时,使用类的成员函数

class C {
fun foo() { println("member") }
}

fun C.foo() { println("extension") }


c.foo()是打印结果为”member”,而不是”extension”.

扩展函数重载成员函数

当扩展函数和成员函数的函数签名不同时,两者互不干扰。

class C {
fun foo() { println("member") }
}

fun C.foo(i: Int) { println("extension") }


调用C().foo(1) ,打印结果”extension”

可空扩展接收者

可以为可空类型增加扩展,在对象为空时也可以调用,在扩展函数中用this==null判断是否为空。

这是Kotlin允许调用空对象的toString的原理。

fun Any?.toString(): String {
if (this == null) return "null"
// after the null check, 'this' is autocast to a non-null type, so the toString() below
// resolves to the member function of the Any class
return toString()
}


扩展属性

和扩展函数类似,也是被扩展类型.属性名.

必须通过自定义访问器实现。

val <T> List<T>.lastIndex: Int
get() = size - 1


由于扩展并不是给原类型增加成员,所以不支持属性的后备字段,不支持初始化器。

扩展属性的行为是通过自定义访问器实现的。

伴生对象扩展

如果类有伴生对象,可以给伴生对象扩展属性和函数

class MyClass {
companion object { }  // will be called "Companion"
}

fun MyClass.Companion.foo() {
// ...
}


伴生对象的扩展成员的调用方法和普通成员相同,类名.扩展成员名

扩展的作用域

大多数情况下扩展定义在顶层,例如包中

package foo.bar

fun Baz.goo() { ... }


在定义的包外使用需要导入

package com.example.usage

import foo.bar.goo // 导入方法1:导入名为“goo”的扩展成员

import foo.bar.*   // 导入方法2:导入foo.bar中的所有内容

fun usage(baz: Baz) {
baz.goo()
}


声明扩展为自己的成员

在类内部声明的其他类扩展,可以直接调用类中的成员,无需.声明。

声明扩展的类的实例叫做分发接收者

被扩展类的实例叫做扩展接收者

class D {
fun bar() { ... }
}

class C {
fun baz() { ... }

fun D.foo() {
bar()   // calls D.bar
baz()   // calls C.baz
}

fun caller(d: D) {
d.foo()   // call the extension function
}
}


重名时被扩展的类的成员优先被调用,可以使用this关键字调用分发接受者的成员。

class C {
fun D.foo() {
toString()         // calls D.toString()
this@C.toString()  // calls C.toString()
}
}


声明为成员的扩展可以是open的,可以被子类重写。

对分发接收者而言是动态分配的。

对扩展接受者依然是静态分配的。

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()   // call the extension function
}
}

class C1 : C() {
override fun D.foo() {
println("D.foo in C1")
}

override fun D1.foo() {
println("D1.foo in C1")
}
}

C().caller(D())   // prints "D.foo in C"
C1().caller(D())  // prints "D.foo in C1" - dispatch receiver is resolved virtually
C().caller(D1())  // prints "D.foo in C" - extension receiver is resolved statically


上例中,对于C和C1是动态分配的,C1中D
a658
.foo被重写,caller打印出的是C1重写的D.foo。而对于caller函数中的d参数,由于是静态分配的,无论传入D的实例还是D1的实例,都会调用函数签名中D类的扩展函数D.foo,而不会去调用D1.foo。

为什么要使用扩展

为某些类添加工具方法。

例如Java中的各种工具类,以java.utilCollections为例,使用时需要用Collections.方法名(集合实例,参数)这样调用。

Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list));
// import static
swap(list, binarySearch(list, max(otherList)), max(list));


直接写成实例的方法最好

list.swap(list.binarySearch(otherList.max()), list.max());


但是又不想在List中添加所有可能使用的方法,这时可以使用扩展。xu
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Kotlin