Kotlin新手教程五(扩展)
创始人
2024-05-26 04:41:52

一、扩展

在Kotlin中可以给一个类添加一个新的方法而不用继承该类或者使用设计模式,这样的方法称为扩展。

1.扩展函数

声明一个扩展函数,我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀。 下面代码为 MutableList 添加一个swap 函数:

fun MutableList.swap(index1: Int, index2: Int) {val tmp = this[index1] // “this”对应该列表this[index1] = this[index2]this[index2] = tmp
}

这个 this 关键字在扩展函数内部对应到接收者对象(传过来的在点符号前的对象) 现在,我们对任意 MutableList 调用该函数了:

val list = mutableListOf(1, 2, 3)
list.swap(0, 2) // “swap()”内部的“this”会保存“list”的值

我们也可以使用泛型进一步升级它:

fun  MutableList.swap(index1: Int, index2: Int) {val tmp = this[index1] // “this”对应该列表this[index1] = this[index2]this[index2] = tmp
}

2.扩展是静态解析的

扩展听起来很强大,但实际不是真正的修改他们所拓展的类,仅仅是可以通过使用该类型的变量用点表达式调用新函数。例:

fun main(args: Array) {val b=B()println(b.getName())
}
open class A
class B:A()
fun A.getName()="A"
fun B.getName()="B"

输出结果为B;这个例子中B为A的子类,且都给两个类拓展了同名方法getName,但是输出结果为何是B。这就是因为扩展函数式静态分发的:调用的扩展函数是由函数调用所在的表达式的类型来决定的, 而不是由表达式运行时求值结果决定的。
当扩展函数和类中原有的函数一致时,最终会调用成员函数:

fun main(args: Array) {val a = A()a.myFun()
}class A {fun myFun() {println("A")}
}fun A.myFun() {println("花里胡哨")
}

输出结果为A
但是如果方法名相同,参数列表不同,就相当于方法重载,就是传入参数符合哪个运行哪个

3.可空接受者

注意可以为可空的接收者类型定义扩展。这样的扩展可以在对象变量上调用, 即使其值为 null,并且可以在函数体内检测 this == null,这能让你在没有检测 null 的时候调用 Kotlin 中的toString():检测发生在扩展函数的内部。

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

4.扩展属性

与函数一样,属性也可以进行扩展,但是由于扩展没有将实际的成员变量插入类中,因此对扩展属性来说幕后字段是无效的。

5.伴生对象的扩展

如果一个类定义有一个伴生对象 ,你也可以为伴生对象定义扩展函数与属性。就像伴生对象的常规成员一样, 可以只使用类名作为限定符来调用伴生对象的扩展成员:

class MyClass {companion object { }  // 将被称为 "Companion"
}fun MyClass.Companion.printCompanion() { println("companion") }fun main() {MyClass.printCompanion()
}

6.扩展的作用域

大多数时候我们在顶层定义扩展——直接在包里:

package org.example.declarationsfun List.getLongestString() { /*……*/}

要使用所定义包之外的一个扩展,我们需要在调用方导入它:

package org.example.usageimport org.example.declarations.getLongestStringfun main() {val list = listOf("red", "green", "blue")list.getLongestString()
}

7.扩展声明为成员

fun main(args: Array) {Connection(Host("aaa"),443).connect()
}class Host(val hostname: String) {fun printHostname() {println(hostname)}
}
class  Connection(val host: Host,val port: Int){fun printPort(){println(port)}fun Host.printConnectionString(){printHostname()print(":")printPort()}fun connect(){host.printConnectionString()}
}

上例中在Connection类中扩展了Host的方法,所以调用该类的扩展方法时能够使用,但是如果调用Host(“kotl.in”).printConnectionString(443)则会报错。
对于分发接收者与扩展接收者的成员名字冲突的情况,扩展接收者优先。要引用分发接收者的成员你可以使用 限定的 this 语法。

class Connection {fun Host.getConnectionString() {toString()         // 调用 Host.toString()this@Connection.toString()  // 调用 Connection.toString()}
}

声明为成员的扩展可以声明为 open 并在子类中覆盖。这意味着这些函数的分发对于分发接收者类型是虚拟的,但对于扩展接收者类型是静态的。

open class Base { }class Derived : Base() { }open class BaseCaller {open fun Base.printFunctionInfo() {println("Base extension function in BaseCaller")}open fun Derived.printFunctionInfo() {println("Derived extension function in BaseCaller")}fun call(b: Base) {b.printFunctionInfo()   // 调用扩展函数}
}class DerivedCaller: BaseCaller() {override fun Base.printFunctionInfo() {println("Base extension function in DerivedCaller")}override fun Derived.printFunctionInfo() {println("Derived extension function in DerivedCaller")}
}fun main() {BaseCaller().call(Base())   // “Base extension function in BaseCaller”DerivedCaller().call(Base())  // “Base extension function in DerivedCaller”——分发接收者虚拟解析DerivedCaller().call(Derived())  // “Base extension function in DerivedCaller”——扩展接收者静态解析
}

上一篇:Kotlin新手教程四(抽象类和接口)

相关内容

热门资讯

猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...