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

扩展

Kotlin官网:Classes and Objects-Extensions
Kotlin支持在一个类使用“扩展”增加功能,无需继承或者使用装饰模式之类的设计模式。
支持扩展函数和扩展属性。

扩展函数

声明扩展函数的格式为:被扩展类型.扩展函数名
下例为给MutableList类型增加swap函数

fun MutableList.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  MutableList.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.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,Kotlin)