Kotlin-Learning 扩展
与 C#,Gosu 类似。kotlin 可以不继承父类,也不使用装饰器模式对原有的类进行扩展,添加新的功能。
Kotlin 支持函数扩展和属性扩展
扩展函数
package classandobject// MutableList<Int> 作为 swap 函数的接收者。 // 交换两个下标的位置 fun MutableList<Int>.swap(index1: Int, index2: Int) {val temp = this[index1]this[index1] = this[index2]this[index2] = temp } 复制代码扩展函数的前缀是这个函数的接收者。 上面的扩展函数给 MutableList 添加了一个新功能 swap, 交换 2 个数的位置。
扩展是被静态解析的
扩展没有修改它所扩展的类,定义一个扩展,并没有插入一个新的成员。只是能让这个类的实例通过 . 调用扩展函数。
扩展函数是静态分发的。
举个例子:
open class Cclass D:C()fun C.foo() = "c" fun D.foo() = "d"fun printFoo(c:C){println(c.foo()) }输出:c 复制代码虽然传进了 D 的实例作为参数,foo 并不是 D 的一个成员函数,和 D 没关系。这里由声明参数的类型决定调用谁的方法,这里声明了 C 类型,就会调用 C 的 foo()。
同名的成员函数优先级大于扩展函数:
class E {fun foo() {println("member")} }fun E.foo() {println("extension") } // 输出 member 复制代码接收者为空的情况
fun Any?.toString(): String {if (this == null) {return "null"}return toString() } 复制代码要扩展的类可能为 null。可以对其做 null 的判断处理。
扩展属性
// 属性扩展, 必须定义访问器 val <T> List<T>.lastIndex: Intget() = size - 1复制代码扩展属性不会真正给类添加一个属性,没法让这个属性有一个 backing field,所以不可以初始化。只能通过 set,get 定义。
伴随对象扩展
如果一个类定义了伴随对象,也可以为这个伴随对象定义扩展函数和扩展属性。
// 为伴随对象定义扩展函数和扩展属性 class CompanionClass{companion object{// ...} }fun CompanionClass.Companion.foo(){println("CompanionClass.Companion.foo()") } 复制代码使用 类名.方法() 直接调用:
CompanionClass.foo() 复制代码扩展的范围
大部分都是用在包名下。
package foo.barfun Baz.goo() { ... } 复制代码用在别的包名下,需要导包 import
将扩展作为成员声明
class D1 {fun bar(){//...println("D1.bar()")} }// 在 C1 类声明 D1 类的扩展函数 foo() class C1 {private fun baz(){println("C1.baz()")}fun D1.foo(){bar() // calls D1.barbaz() // call C1.baz}fun caller(d:D1){d.foo()} } 复制代码将 D1 的扩展函数作为成员声明在 C1 中。此时 D1 叫 extension receiver.C1 负责分发,叫 dispatch receiver。
在 C1 中的 foo() 扩展函数既可以调用 D1 中的函数,也可以调用 C1 中的函数,如果同名的话,优先调用 D1 中的。 同名的属性也是一样。
为了调用 C1 中的同名函数,可以用 this 限定符。
class C {fun D.foo() {toString() // calls D.toString()this@C.toString() // calls C.toString()} 复制代码dispath receiver 类型是动态的
定义在类中的扩展函数可以被子类重写。
// receiver type 是静态的。 dispatch receiver type 是动态的 open class Rclass R1 : R()open class T {open fun R.foo() {println("R.foo in T")}open fun R1.foo() {println("R1.foo in T")}fun caller(r: R) {r.foo()} }class T1 : T() {override fun R.foo() {println("R.foo in T1")}override fun R1.foo() {println("R1.foo in T1")} }// 调用 T().caller(R()) T().caller(R1())T1().caller(R()) T1().caller(R1())// 输出 R.foo in T R.foo in T R.foo in T1 R.foo in T1 复制代码前面也提到扩展函数动动态解析的。由于 caller 中参数定义了类型为 R 所以只会调用 R 的扩展函数。而分发者的类型是动态的。
为什么要有扩展函数
在 java 中我们会在一个类中写很多静态方法作为工具类。FileUtils,StringUtils...
在 Java 代码中。有时候在一句代码中需要使用一个工具类的多个方法,就会变成这样:
Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list)); 复制代码可以使用静态导包(static import)的方式解决:
swap(list, binarySearch(list, max(otherList)), max(list)); 复制代码list 看起来重复。
这样写更简洁,把所有的方法都归纳到 list 内当中,就不需要每个地方都传一个 list 对象。
list.swap(list.binarySearch(otherList.max()), list.max()); 复制代码但是不可能在 List 中实现所有的方法。但是 Kotlin 的扩展可以做到,这就是为什么需要扩展的原因。
《新程序员》:云原生和全面数字化实践50位技术专家共同创作,文字、视频、音频交互阅读总结
以上是生活随笔为你收集整理的Kotlin-Learning 扩展的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: Source Insight 4.0常用
- 下一篇: saltstack管理saltstack