kotlin doc 之 扩展(Extensions)

扩展

kotlin和C#相似,提供了扩展类的能力,通过使新的函数,而不是通过继承或者装饰这几模式。kotlin支持extentions方法和extentions属性。

扩展方法

为了声明一个扩展方法,我们需要一个接收类型的样板。下面增加了一耳光swap方法到MutableList :

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

关键字this在一个扩展函数中,关联接收类型。现在我们可以在MutableList调用这样一个方法

val l = muitableListOf(1,2,3)
l.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 C
class D: C()
fun C.foo() = "C"
fun D.foo() = "D"

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

printFoo(D())

这个案例的记过将打印“C”,因为这个被调用的扩展函数依赖于声明的参数类型那个c,它是C class

如果一个类有一个成员函数,并且扩展函数也被定义为相同的接收类型那个,这是优先调用成员

class C{
    fun foo(){
        print("memeber")
    }

}

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

结果是 “memeber”

如果两者签名不同,将按着方法签名调用

class C {
    fun f(){
        print("memeber")
    }

}

fun C.f(a : Int){
    print("extention")
}

可空的接收者

注意 扩展可以被定义为 一个可空的接收类型那个。这样的扩展,可以被一个空对象条用,能够在方法体内检查对象是否非空。

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

扩展属性

和方法相似,属性也支持扩展

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

注意,扩展的本质并不是将成员插入到类中,因此没有一个有效的方式扩展属性,达到“backing field”的效果。这就是为什么初始化器不能初始化扩展属性。它们只能定义getter和setter方法。

val Foo.bar = 1 //这是错误的

友元对象的扩展

如果一个类已经顶一个友元对象,你可以针对它进行扩展。

class MyClass{
    companion object {}
}

fun MyClass.Companion.foo(){

}

并且像componion oject的普通成员一样,它们可以通过类名进行调用

MyClass.foo()

扩展范围

通常我们是包下,进行顶级扩展

package foo.bar

fun Baz.goo(){}

为了在包外使用,我们需要进行导包。

package com.example.useage
import foo.bar.goo 
import foo.bar.* //包下所有内容

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

声明扩展作为成员

在一个类中,你可以为另一个声明扩展。在这样的一个扩展的内部,有许多隐式的不用标识符的直接访问的接收对象成员。扩展所在类的实例被调用。

class D{
    fun bar(){}
}

class C{
    fun baz(){}
    fun D.foo(){
        bar()
        baz()
    }

    fun caller(d :D){
        d.foo()
    }
}

有一种情况–分发接收者类内部的成员与扩展接收者中是冲突,则需要进行如下指明:

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

如果成员扩展被声明为open,则可以被继承。在这里要注意的是 分发接收者是可以支持多态的,但是扩展接收者是 静态的

open class D{

}

class D1 : D(){

}

open class C{
    open fun D.foo(){
        print("D.foo in C")
    }

    open fun D1.foo(){
        print("D1.foo in C")
    }

    fun caller(d:D){
        d.foo()
    }
}


class C1 :C(){
      open fun D.foo(){
        print("D.foo in C1")
    }

    open fun D1.foo(){
        print("D1.foo in C1")
    }
}


C().caller(D())
C1().caller(D()) //分发 使用的是多态
C().caller(D1())//扩展使用的是静态

动机

在java中,我们习惯命名各种utls类。最著名的是java.util.Collections.但是它们的使用却是如下格式:

//java
Colletions.swap(list, Collections.binarySearch(list, Collections.max(otherList),Collections.max(list));

这些类的名字,可以同过静态导入:

//java
swap(list, binarySearch(list,max(otherList)),max(list));

在高度优秀IDE自动面前,这样的有优化显然是微弱的。理想格式如下:

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

但是我们不想在List中实现所有的扩展方法,这就是 扩展可以帮我们做的事情。

猜你喜欢

转载自blog.csdn.net/dirksmaller/article/details/80674176
doc