Kotlin 从学习到 Android 第十章 扩展
来源:互联网 发布:做什么网络兼职赚钱 编辑:程序博客网 时间:2024/06/16 21:07
与 C# 、Gosu 类似,Kotlin 也可以为一个类扩展一个新的功能,而不需要从类继承或使用任何类型的设计模式,如装饰( Decorator )。这是通过一种被称为扩展的特殊声明完成的。Kotlin 支持扩展函数和扩展属性。
扩展函数
声明扩展函数时,我们需要在函数名前加上一个接收类型,也就是被扩展的类型。例如:下面的代码为 MutableList 添加一个 swap 的新函数。
fun MutableList<Int>.swap(index1: Int, index2: Int) { val tmp = this[index1] this[index1] = this[index2] this[index2] = tmp}
上面代码中的 this 代表的是 MutableList 。扩展了 swap 函数后,MutableList 类型的数据就可以直接使用 swap 函数了:
val l = mutableListOf(1, 2, 3)l.swap(0, 2)
当然在扩展函数中我们也可以使用泛型:
fun <T> MutableList<T>.swap(index1: Int, index2: Int) { val tmp = this[index1] // 'this' corresponds to the list this[index1] = this[index2] this[index2] = tmp}
对于不熟悉 Kotlin 的童鞋,上面的代码可能看不明白,下面我们为自己的类来扩展一个函数用以演示 Kotlin 的扩展功能:
fun main(args: Array<String>) { var ec = ExtensionedClass() ec.nameSetter("admin") ec.alertName() // 这里可以直接调用扩展函数 println(ec.name) // hello admin , 可见扩展函数调用成功}// 为 ExtensionedClass 添加一个新的函数fun ExtensionedClass.alertName(){ this.name = "hello " + this.name}class ExtensionedClass{ var name: String? = null fun nameSetter(str: String){ name = str }}
扩展解析静态
扩展实际上并不修改它们扩展的类。通过定义一个扩展,你不需要将新成员插入到一个类中,而仅仅是使用这个类型的 变量.扩展函数 来调用。
我们想要强调的是,扩展函数是静态分派的,也就是说,它们不是由接收者类型来实现的。这意味着被调用的扩展函数是由调用函数的表达式的类型决定的,而不是在运行时对表达式求值的结果。例如:
open class Cclass D: C()fun C.foo() = "c"fun D.foo() = "d"fun printFoo(c: C) { println(c.foo())}printFoo(D()) // c
打印结果为 c ,因为调用的扩展函数只依赖于已声明的参数 C 类型,即 C 类。
如果一个类有一个成员函数,并且一个扩展函数被定义为具有相同的接收者类型,相同的名称,并且适用于给定的参数,那么在调用时总会调用成员函数。例如:
class C { fun foo() { println("member") }}fun C.foo() { println("extension") }
如果我们调用 c.foo() ,将会打印 “member”, 而不是 “extension”。
但是,对于扩展函数来说,重载成员函数是完全可以的,这些函数具有相同的名称但是不同的签名:
class C { fun foo() { println("member") }}fun C.foo(i: Int) { println("extension") }
如果调用 C().foo(1) 将会打印 “extension”。
可 null 接收器
注意,可以用可空的接收方类型定义扩展。这样的扩展可以在对象变量上调用,即使它的值为 null ,并且可以在函数体中检查 this == null ,这将允许你在 Kotlin 中调用 toString() 函数而不用检查是否为 null : 因为在扩展函数中检测了。
fun Any?.toString(): String { if (this == null) return "null" // 检测完是否是 null 后,下面的 this 会自动转化为非 null 类型 return toString()}
扩展属性
扩展属性和扩展函数类似:
val <T> List<T>.lastIndex: Int get() = size - 1
注意,由于扩展实际上并没有将成员插入到类中,所以对于扩展属性来说没有有效的方法来拥有一个支持字段(backing field)。这就是为什么初始化器不允许扩展属性的原因。它们的行为只能通过显式地提供getter/setter来定义。例如:
val Foo.bar = 1 // 错误: 扩展属性不能被初始化
Companion 对象扩展
如果一个类有 companion 对象,那么你也可以为这个 companion 对象定义扩展函数和扩展属性:
class MyClass { companion object { } // will be called "Companion"}fun MyClass.Companion.foo() { // ...}
就像 companion 对象的常规成员一样,可以只使用类名作为限定符来调用它们:
MyClass.foo()
扩展的作用范围
大多数时候,我们在顶层定义扩展,也就是直接在包下面:
package foo.barfun Baz.goo() { ... }
要在其声明包之外使用这种扩展,我们需要在调用点上导入它
package com.example.usageimport foo.bar.goo // 只引入 goo // 或者import foo.bar.* // 全部引入fun usage(baz: Baz) { baz.goo()}
声明扩展成员
在一个类中,您可以为另一个类声明扩展。在这样的扩展中,有多个隐式接收器-对象成员可以在没有限定符的情况下被访问。扩展被声明的类的实例称为分派接收器,而扩展方法的接收者类型的实例称为扩展接收器。
class D { fun bar() { ... }}class C { fun baz() { ... } fun D.foo() { bar() // calls D.bar baz() // calls C.baz } fun caller(d: D) { d.foo() // call the extension function }}
如果在分派接收器和扩展接收器之间发生名称冲突,则扩展接收器优先。要引用调度接收器的成员,你可以使用限定语法。
class C { fun D.foo() { toString() // D.toString() this@C.toString() // C.toString() }
声明为成员的扩展可以在子类中声明为 open 和 overridden 。这意味着分派这些函数对于分派接收器类型是虚拟的,但是对于扩展接收器类型来说是静态的。
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") }}C().caller(D()) // "D.foo in C"C1().caller(D()) // "D.foo in C1" - dispatch receiver is resolved virtuallyC().caller(D1()) // "D.foo in C" - extension receiver is resolved statically
使用扩展功能的动机
在 Java 中, 我们通常命名一些工具类,如:FileUtils, StringUtils 等等。著名的类 java.util.Collections 也是属于这种风格。但是当使用这些工具类时,会让我们感觉不舒服,例如:
// JavaCollections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list))
当然,为了简化代码,我们可以通过静态导入达到下面的效果:
// Javaswap(list, binarySearch(list, max(otherList)), max(list))
这稍微好一点,但是 IDE 强大的代码补全功能对我们的帮助还是太少。如果能像下面这样就好了:
// Javalist.swap(list.binarySearch(otherList.max()), list.max())
但是,我们又不想在类中实现完所有有可能用到的方法,所以,这就是扩展功能的优势所在。
- Kotlin 从学习到 Android 第十章 扩展
- Kotlin 从学习到 Android 第二章 习惯用法
- Kotlin 从学习到 Android 第三章 编码规范
- Kotlin 从学习到 Android 第四章 控制流
- Kotlin 从学习到 Android 第五章 返回和跳转
- Kotlin 从学习到 Android 第六章 类和继承
- Kotlin 从学习到 Android 第七章 属性和字段
- Kotlin 从学习到 Android 第八章 接口
- Kotlin 从学习到 Android 第十三章 对象
- Kotlin 从学习到 Android 第一章 基础语法
- Kotlin 从学习到 Android 第九章 可见性修饰符
- Kotlin 从学习到 Android 第十一章 枚举类、嵌套类 和 密封类
- Kotlin Android 扩展
- android kotlin扩展函数
- Android扩展Kotlin
- 从Java到Kotlin
- 从Java到Kotlin
- 《Kotlin 程序设计》第十章 Kotlin Native介绍
- Android:答题APP的设计与实现(mysql+jsp+Android)
- 还债系列之数据结构——栈和队列
- Linux-鸟菜-7-Linux文件系统-EXT
- Button(实训)
- NiFi开发教程之--RouteOnAttribute(路由属性)
- Kotlin 从学习到 Android 第十章 扩展
- SVN使用详解
- git
- Android Html.fromHtml(String)过时的替代方法
- 软件自动化 接口测试框架干货,群友写的(python)有Excel输出报告,也有读取Excel接口用例文件
- Apriori算法详解
- python3读写csv数据
- 每天学一点Swift----面向对象下(八)
- linux网络编程之posix线程(一)