Расширения Kotlin

Kotlin позволяет расширять свойства и методы класса без необходимости наследования или паттерна Декоратор.

Расширение — это статическое поведение, и оно не влияет на код самого расширяемого класса.


Функции-расширения

Функции-расширения позволяют добавлять новые методы к существующим классам без изменения исходного класса. Форма определения функции-расширения:

fun receiverType.functionName(params){
    body
}
  • receiverType: указывает получателя функции, то есть объект, расширяемый функцией
  • functionName: имя функции-расширения
  • params: параметры функции-расширения, могут быть NULL

В следующем примере расширяется класс User:

class User(var name:String)

/**扩展函数**/
fun User.Print(){
    print("用户名 $name")
}

fun main(arg:Array<String>){
    var user = User("Example")
    user.Print()
}

Результат выполнения примера:

用户名 Example

Следующий код предназначен для MutableListчтобы добавить функцию swap:

// 扩展函数 swap,调换不同位置的值
fun MutableList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1]     //  this 对应该列表
    this[index1] = this[index2]
    this[index2] = tmp
}

fun main(args: Array<String>) {

    val l = mutableListOf(1, 2, 3)
    // 位置 0 和 2 的值做了互换
    l.swap(0, 2) // 'swap()' 函数内的 'this' 将指向 'l' 的值

    println(l.toString())
}

Результат выполнения примера:

[3, 2, 1]

Ключевое слово this ссылается на объект-получатель (то есть на экземпляр объекта, указанный перед точкой при вызове функции-расширения).


Функции-расширения разрешаются статически

Функции-расширения разрешаются статически; они не являются виртуальными членами типа-получателя. При вызове функции-расширения то, какая именно функция вызывается, определяется выражением объекта, вызывающего функцию, а не динамическим типом:

open class C

class D: C()

fun C.foo() = "c"   // 扩展函数 foo

fun D.foo() = "d"   // 扩展函数 foo

fun printFoo(c: C) {
    println(c.foo())  // 类型是 C 类
}

fun main(arg:Array<String>){
    printFoo(D())
}

Результат выполнения примера:

c

Если функция-расширение и функция-член идентичны, при использовании этой функции предпочтение отдаётся функции-члену.

class C {
    fun foo() { println("成员函数") }
}

fun C.foo() { println("扩展函数") }

fun main(arg:Array<String>){
    var c = C()
    c.foo()
}

Результат выполнения примера:

Member function

Расширение null-объекта

Внутри функции-расширения можно использовать this, чтобы определить, является ли получатель NULL. Таким образом, даже если получатель NULL, функцию-расширение всё равно можно вызвать. Например:

fun Any?.toString(): String {
    if (this == null) return "null"
    // 空检测之后,“this”会自动转换为非空类型,所以下面的 toString()
    // 解析为 Any 类的成员函数
    return toString()
}
fun main(arg:Array<String>){
    var t = null
    println(t.toString())
}

Результат выполнения примера:

null

Свойства-расширения

Помимо функций, Kotlin также поддерживает расширение свойств с помощью свойств:

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

Свойства-расширения можно определять в классах или файлах Kotlin, но нельзя определять в функциях. Поскольку у свойств-расширений нет backing field, их нельзя инициализировать; их можно определить только с помощью явно предоставленных геттеров/сеттеров.

val Foo.bar = 1 // 错误:扩展属性不能有初始化器

Свойства-расширения можно объявлять только как val.


Расширения для объектов-компаньонов

Если класс определяет объект-компаньон, вы также можете определить функции-расширения и свойства-расширения для этого объекта-компаньона.

К сопутствующему объекту обращаются в форме "Class.". Функции-расширения, объявленные для сопутствующего объекта, вызываются с использованием квалификатора имени класса:

class MyClass {
    companion object { }  // 将被称为 "Companion"
}

fun MyClass.Companion.foo() {
    println("伴随对象的扩展函数")
}

val MyClass.Companion.no: Int
    get() = 10

fun main(args: Array<String>) {
    println("no:${MyClass.no}")
    MyClass.foo()
}

Результат выполнения примера:

no:10
伴随对象的扩展函数

Область видимости расширений

Обычно функции-расширения или свойства-расширения определяются в пакете верхнего уровня:

package foo.bar

fun Baz.goo() { …… } 

Чтобы использовать расширение извне пакета, в котором оно определено, импортируйте имя функции-расширения с помощью import:

package com.example.usage

import foo.bar.goo // 导入所有名为 goo 的扩展
                   // 或者
import foo.bar.*   // 从 foo.bar 导入一切

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

Расширения, объявленные как члены

Внутри класса можно объявлять расширения для другого класса.

В таком расширении есть несколько неявных получателей. Экземпляр класса, в котором определён метод-расширение, называется диспетчерским получателем, а экземпляр целевого типа метода-расширения — получателем расширения.

class D {
    fun bar() { println("D bar") }
}

class C {
    fun baz() { println("C baz") }

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

    fun caller(d: D) {
        d.foo()   // 调用扩展函数
    }
}

fun main(args: Array<String>) {
    val c: C = C()
    val d: D = D()
    c.caller(d)

}

Результат выполнения примера:

D bar
C baz

Внутри класса C создаётся расширение для класса D. В этом случае C называется диспетчерским получателем, а D — получателем расширения. Из приведённого выше примера хорошо видно, что в функции-расширении можно вызывать функции-члены диспетчерского получателя.

Если вызывается функция и она существует как в диспетчерском получателе, так и в получателе расширения, приоритет имеет получатель расширения. Чтобы сослаться на члены диспетчерского получателя, можно использовать синтаксис квалифицированного this.

class D {
    fun bar() { println("D bar") }
}

class C {
    fun bar() { println("C bar") }  // 与 D 类 的 bar 同名

    fun D.foo() {
        bar()         // 调用 D.bar(),扩展接收者优先
        [email protected]()  // 调用 C.bar()
    }

    fun caller(d: D) {
        d.foo()   // 调用扩展函数
    }
}

fun main(args: Array<String>) {
    val c: C = C()
    val d: D = D()
    c.caller(d)

}

Результат выполнения примера:

D bar
C bar

Функции-расширения, определённые как члены, можно объявлять как 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()   // 调用扩展函数
    }
}

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

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


fun main(args: Array<String>) {
    C().caller(D())   // 输出 "D.foo in C"
    C1().caller(D())  // 输出 "D.foo in C1" —— 分发接收者虚拟解析
    C().caller(D1())  // 输出 "D.foo in C" —— 扩展接收者静态解析

}

Результат выполнения примера:

D.foo in C
D.foo in C1
D.foo in C
Другие расширения