Kotlin-拓展功能

来源:互联网 发布:金融软件供应商 编辑:程序博客网 时间:2024/06/05 07:39

Kotlin跟C#和Gosu一样简洁,提供了在不必继承类或使用任何类型的设计模式(如装饰器)的情况下扩展新功能的类的能力。 这是通过称为扩展的特殊声明完成的。Kotlin支持扩展函数和扩展属性

扩展函数

声明拓展函数,我们需要在接收器类型名称的后面像后缀一样添加,以下就是一个例子,在MutableList后面添加一个swap拓展函数:

fun MutableList<Int>.swap(index1 :Int,index2 :Int){    val tmp=this[index1];    this[index1]=this[index2];    this[index2]=tmp;}

在这个拓展函数中的this代表的是这个接收对象(就是点号前面的对象).从上面那样定义拓展函数后,我们就可以使用了:

val list=mutableListOf(1,2,3)list.swap(0,2);

当然这个函数我们可以设计得更通用些,比如下面这样:

fun <T> MutableList<T>.swap(index1:Int,index2:Int){    val tmp=this[index1];    this[index1]=this[index2];    this[index2]=tmp}

我们在函数名前面定义了一个泛型参数,方便在接收器中确定类型。

静态的拓展解决方案

事实上,拓展函数在定义之后就不方便更改了.定义拓展函数后,就不可以插入新成员到类中了,但是可以通过这种方式来调用新函数。
在这里,我们强调的是,拓展函数是静态的。也就是说,对于接收器类型来说,拓展函数不是虚拟的。这就说明,拓展函数会被调用是由表达式来确定,而不是由表达式计算结果的类型来确定。

open class Cclass D:C()fun C.foo()="c"fun D.foo()="d"fun printFoo(c:C){    println(c.foo());}printFoo(D());

这个例子中将会打印’c’,因为拓展函数(printFoor)的调用依赖于它的参数(c:C)
如果一个类中,有成员函数和拓展函数同名,那么成员函数的优先级总是比较高,总会是优先。

class E{    fun foor(){        println("member");    }}fun C.foor(){    println("extension");}

当E类的所有实例调用 foor的时候,总会调用到成员函数。然而,相同名字也可以区分,比如可以用不同参数来区别,

class F{    fun foo(){        println("I am member");    }}fun foo(i:Int){    println("I am extension $i");}

进行调用的时候,F().foo()是成员函数,而F().foo(12)则是拓展函数

可为空的接收器

这里有一个需要注意的地方就是,拓展函数可以定义在空的接收器类型上。对象的实例在为空的情况下还是可以调用拓展函数,但在函数中需要检查实例是否非空,进行不同的处理,从这里可以知道,在Kotlin中,你可以不用检查非空就可以使用toString(),非空检查可以放在内部的拓展函数里面。

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

拓展属性

和拓展函数一样,Kotlin也支持拓展属性:

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

需要注意的是,添加拓展属性之后就不要再插入成员(变量和成员属性)了,拓展属性没有支持隐性字段的方法,这就是为什么不允许初始化拓展属性.他们更改的动作只能通过明确的getter和setter来操作。

val Foo.bar=1 //这是错误的方式,不允许这样初始化拓展属性

拓展衍生对象(Companion Object Extensions)

如果一个类定义了衍生对象,你也可以定义对应的拓展函数和拓展属性

class MK{    companion object{}}fun MK.Companion.food(){    //...}

这个衍生对象的调用和成员方法的调用有点小区别,他们只能通过类名来直接调用

MK.food()

拓展的范围

多数情况下,我们定义拓展都是直接定义在顶级,比如定义在包下:

package com.mkfun MK.operation(){...}

在其他包内使用这些拓展,我们需要导入

package com.mk.utilimport com.mk.MK.operationfun usage(mk:MK){    mk.operation()}

把拓展当做成员来声明

在类的内部,你可以把拓展声明成另外一个类,在拓展内部,有多个隐式接收器,可以不需要修饰符就可以访问对象成员.声明拓展的类的实例称为分派接收器.拓展方法的接收器实例则称为拓展接收器

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    }}

当遇上分派接收器和拓展接收器的成员名冲突时,拓展接收器的成员优先级比较高,要引用分派接收器的成员,你可以使用限定的语法

class C {    fun D.foo() {        toString()         // 调用 D.toString()        this@C.toString()  // 调用 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 virtuallyC().caller(D1())  // prints "D.foo in C" - 拓展接收器是静态调用

设计拓展的缘由

在Java中,我们经常用”*Utils”来命名:FileUtils,StringUtils等,著名的java.util.Collections就是一个示例,对于这些工具类,我们这样用:

//JavaCollections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list))

那些类名让我们用起来很不爽,我可以用静态导入:

//Javaswap(list, binarySearch(list, max(otherList)), max(list))

到这里为止,已经好一点了,但我们还是没有在IDE强大的代码辅助功能中得到什么帮助,如果还有更好,那就是下面这样:

// Javalist.swap(list.binarySearch(otherList.max()), list.max())

但我们并不想全部都这样来实现,那么这时候,拓展就可以帮助我们了。

原创粉丝点击