Kotlin笔记(扩展)
Kotlin 提供了对一个类扩展新功能的能力,而无需从类继承或使用 Decorator 等设计模式。这是通过称为 扩展 的特殊声明完成的。例如,您可以为无法修改的第三方库中的类编写新函数。可以以通常的方式调用此类函数,就好像它们是原始类的方法一样。这种机制称为 扩展功能 。您也可以为现有的类扩展新的属性。
扩展功能
要声明扩展函数,请在其名称前加上 接收器类型 ,它指的是被扩展的类型。以下为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对应于接收器对象(在点之前传递的对象)。现在,您可以在 任何MutableList对象上调用这样的函数: val list = mutableListOf(1, 2, 3) list.swap(0, 2) // "this" inside "swap()" will hold the value of "list"
此函数对任何MutableList对象都有意义,您还可以在其中加入泛型: fun MutableList.swap(index1: Int, index2: Int) { val tmp = this[index1] // "this" corresponds to the list this[index1] = this[index2] this[index2] = tmp }
您需要在函数名称之前声明泛型类型参数,以使其在接收器类型表达式中可用。有关泛型的更多信息,请参阅 泛型函数。
扩展是静态解析的
扩展实际上并不修改它们所扩展的类。通过定义扩展,您不会将新成员插入到类中,而只是使新函数可以使用这种类型的变量的点符号调用。扩展函数是 静态 调度的,被调用的扩展函数是由调用函数的 接收器 类型所对应的 表达式 的决定,而不是由在运行时计算该表达式的结果类型决定。例如: fun main() { open class Shape class Rectangle: Shape() fun Shape.getName() = "Shape" fun Rectangle.getName() = "Rectangle" fun printClassName(s: Shape) { println(s.getName()) } printClassName(Rectangle()) } 输出:Shape
此示例打印 Shape ,因为调用的扩展函数仅取决于参数的声明类型s,即Shape类。
如果一个类有一个成员函数,并且定义了一个具有相同接收器类型、相同名称并且适用于给定参数的扩展函数,则成员函数的优先级更高。例如: class Example { fun printFunctionType() { println("Class method") } } fun Example.printFunctionType() { println("Extension function") } Example().printFunctionType() 输出:Class method
但是,扩展函数完全可以重载同名但签名不同的成员函数: class Example { fun printFunctionType() { println("Class method") } } fun Example.printFunctionType(i: Int) { println("Extension function #$i") } Example().printFunctionType(1) 输出:Extension function #1
可空接收器
请注意,可以使用可为空的接收器类型定义扩展。即使对象变量的值为空,也可以在对象变量上调用这些扩展,并且可以扩展函数内部检查this == null。这样,您可以在不检查null的情况下调用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() }
扩展属性
Kotlin 支持扩展属性,就像它支持函数一样: val List.lastIndex: Int get() = size - 1
由于扩展实际上并不将成员插入到类中,因此扩展属性没有有效的方法来拥有 支持字段。这就是 扩展属性不允许使用初始化程序的 原因。它们的行为只能通过显式提供 getter/setter 来定义。 val House.number = 1 // error: initializers are not allowed for extension properties
伴随对象的扩展
如果一个类定义了 伴随对象,您还可以为伴随对象定义扩展函数和属性。就像 伴随对象的常规成员一样,它们可以仅使用类名作为限定符来调用: class MyClass { companion object { } // will be called "Companion" } fun MyClass.Companion.printCompanion() { println("companion") } fun main() { MyClass.printCompanion() }
扩展的范围
在大多数情况下,您在顶层定义扩展,直接在包下: package org.example.declarations fun List.getLongestString() { /*...*/}
要在其声明包之外使用扩展,请在调用站点将其导入: package org.example.usage import org.example.declarations.getLongestString fun main() { val list = listOf("red", "green", "blue") list.getLongestString() }
将扩展声明为成员
您可以在一个类中声明另一个类的扩展。在这样的扩展中,有多个 隐式接收器 - 其成员可以在没有限定符的情况下访问的对象。声明扩展的类的实例称为 调度接收器 ,扩展方法的接收器类型的实例称为 扩展接收器 。 class Host(val hostname: String) { fun printHostname() { print(hostname) } } class Connection(val host: Host, val port: Int) { fun printPort() { print(port) } fun Host.printConnectionString() { printHostname() // calls Host.printHostname() print(":") printPort() // calls Connection.printPort() } fun connect() { /*...*/ host.printConnectionString() // calls the extension function } } fun main() { Connection(Host("kotl.in"), 443).connect() //Host("kotl.in").printConnectionString() // error, the extension function is unavailable outside Connection } 输出:kotl.in:443
如果调度接收器和扩展接收器的成员之间发生名称冲突,则扩展接收器优先。要引用调度接收者的成员,您可以使用 限定的this语法。 class Connection { fun Host.getConnectionString() { toString() // calls Host.toString() this@Connection.toString() // calls 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() // call the extension function } } 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" - dispatch receiver is resolved virtually DerivedCaller().call(Derived()) // "Base extension function in DerivedCaller" - extension receiver is resolved statically }
注意可见性
扩展使用与在相同范围内声明的常规函数 相同的可见性修饰符。例如: 在文件顶层声明的扩展可以访问同一文件中的其他顶层声明。 如果扩展在其接收者类型之外声明,则它不能访问接收者的private或protected成员。
日本虎牌电饭煲,出了迷你版,一人份米饭也能煮你是不是也经常遇到这样的状况米饭不小心煮多了,隔了几天忘记吃,只好倒掉,浪费粮食买了个很大的电饭煲,就为了实用,其实每次也没煮多少米,放着还占地方换一个小巧好用的电饭煲吧!一条生活
拼多多用户数7。88亿成为中国电商第一北京时间3月17日,拼多多发布2020年第四季度及全年财报截至2020年底,拼多多年活跃买家数达7。884亿,成为中国用户规模最大的电商平台。四季度,拼多多APP平均月活跃用户数达
选择条码扫描器近年来,国内大型购物中心,连锁店等商业企业已经意识到商业POS系统带给商业企业管理的巨大利益,并相继建立了商业POS网络系统。对于网络系统的设计和安装原理,各种业出版物都有详细的介
OPPOReno2超强防抖视频曝光,你爱上这款性价比新机了吗?今天上午,OPPO副总裁沈义人在微博上晒出一段OPPOReno2与其它机型的防抖效果对比视频,可以看出OPPOReno2的主镜头应该少不了OIS光学防抖,预计在算法矫正上也会有所补
华为暂停5G的发展,那世界也就暂停了全华制说实话华为p50的发布让很多人措不及防,麒麟芯片确实是有了,但是多半是以前的残次品。毕竟5G的开发者,在万众瞩目的情况下发布4G手机,这波操作属实把我看得有点迷?但是这次发布
雁鸣湖镇开展用声音传递精彩用自信展现自我志愿服务活动小鲤鱼寻找声音之泉是由残疾大学生,郑州市小水滴帮扶对象王怡文自创自编自导自演的一个励志的儿童广播剧,剧情简介是一群可爱的小鲤鱼受到了赖皮蛇的迫害,小鲤鱼泡泡为了拯救鲤鱼湖,拯救他的
以强大回敬热爱?与小米相比,realmeGTNeo2的亮点还有哪些提到最近热度最高的手机产品,realme真我即将发布的realmeGTNeo2绝对是当下备受网友期待的产品,凭借着前几天的疯狂预热造势,网友们对这款手机的期待值也再次拉满。可以确定
这才是智能冰箱使用效率最大化的方式?松下这三款冰箱值得关注近几年,随着智能家居的快速发展,大家电产品也正逐渐提升着人们的生活质量。或许你从来没有想过智能冰箱能够带来怎样的方便快捷,但其实智能冰箱正潜移默化地提升着你的生活品质,或是提醒你家
加拿大复飞上海后第1班就爆确诊超过50架航班出状况美西时间8月18日,据Narcity报道,随着加拿大国内疫情好转,航班正在陆续增多。但从近日的机场确诊病例数字来看,疫情依旧十分严峻。图多伦多皮尔逊国际机场(图片来源Narcity
铁锅炒菜妙处多,教你如何来开锅随着生活水平的提高,人们越来越重视饮食健康问题,在日常炒菜的时候都会选择那种纯铁锅,因为它没有任何涂层,并且里面还含有铁元素,被人体吸收后对健康十分的有益。那么问题来了,新买的铁锅
卧室现在都流行用这样床了,你家有吗在卧室装修中人们对于床的设计非常的重视,可是对于小户型来说放上一张床就显得十分的拥挤了,家里要是有个小孩让他睡哪呢?这一系列的问题真是让人头疼,那么如何来打破这尴尬局面呢?现在很多