Расширения 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,调换不同位置的值
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Другие расширения