1. 扩展函数
Kotlin的扩展函数可以让你作为一个类成员进行调用的函数,但是是定义在这个类的外部。这样可以很方便的扩展一个已经存在的类,为它添加额外的方法。在Kotlin源码中,有大量的扩展函数来扩展java,这样使得Kotlin比java更方便使用,效率更高。通常在java中,我们是以各种XXXUtils的方式来对已经存在的类进行功能的扩展。但是有了扩展函数,我们就能丢弃让人讨厌的XXXUtils方法工具类。下面举个例子,假如我们需要为String类型添加一个返回这个字符串最后一个字符的方法:
package com.dengyin2000.kotlintest1
fun String.lastChar(): Char = this.get(this.length - 1)
fun main(args: Array) {
println("Kotlin".lastChar())
}
你只需要在你添加的函数名字之前放置你想要扩展的类或者接口的类型。这个类名叫着接收器类型(receiver type),而你调用的扩展函数的值叫做接收器对象(receiver object)。如下图:
接收器类型是扩展定义的类型,而接收器对象是这个类型的实例。调用方式跟普通的函数调用方式一致:
println("Kotlin".lastChar())
在这个例子中,String
是接收器类型,"Kotlin"
接收器对象,在这个扩展函数中,你可以直接访问你扩展的类型的函数和属性,就像定义在这个类中的方法一样,但是扩展函数并不允许你打破封装。跟定义在类中方法不同,它不能访问那些私有的、受保护的方法和属性。
1.1 扩展函数的导入
大多数情况下,我们直接在包里定义扩展函数。这样我们就可以在整个包里面使用这些扩展,如果我们要使用其他包的扩展,我们就需要导入它。导入扩展函数跟导入类是一样的方式。
import com.dengyin2000.kotlintest1.lastChar
或者
import com.dengyin2000.kotlintest1.*
有时候,可能你引入的第三方包都对同一个类型进行了相同函数名扩展,为了解决冲突问题,你可以使用下面的方式对扩展函数进行改名。
package com.dengyin2000.kotlintest2
import com.dengyin2000.kotlintest1.lastChar as last
fun main(args: Array) {
println("Kotlin".last())
}
1.2 范型化的扩展函数
我们也可以在对扩展函数进行范型化。
package com.dengyin2000.kotlintest1
fun String.lastChar(): Char = this.get(this.length - 1)
fun Collection.joinToString(
separator: String = ",",
prefix: String = "",
postfix: String = ""
): String{
val result = StringBuilder(prefix)
for ((index, value) in this.withIndex()) {
if (index > 0) {
result.append(separator)
}
result.append(value)
}
result.append(postfix)
return result.toString()
}
fun main(args: Array) {
println(listOf("a", "b", "c").joinToString(prefix = "[", postfix = "]"))
}
输出:
[a,b,c]
1.3 扩展函数不可覆盖(overriding)
方法的覆盖(overriding)对类中的成员函数是有效的,但是扩展函数不能被覆盖,请看下面这个例子:
package com.dengyin2000.kotlintest1
open class View{
open fun click() {
println("view clicked")
}
}
open class Button: View() {
override fun click(){
println("button clicked")
}
}
fun View.longClick() = println("view longClicked")
fun Button.longClick() = println("button longClicked")
fun main(args: Array) {
val button:View = Button()
button.click()
button.longClick()
}
输出:
button clicked
view longClicked
可以看到扩展函数并不能被覆盖,我们把变量定义成View,longClick()使用的是View.longClick()扩展函数。扩展函数并不是类的一部分,他们申明在类的外部。尽管你可以为某个基类和它的之类用同样的名字和参数来定义扩展函数,被调用的函数依赖已被申明的静态类型,而不是运行时的变量类型。
1.4 Java调用扩展函数
调用一个扩展函数并没有涉及对象的创建或者其他运行时开销,在底层,一个扩展函数是一个接收器对象作为第一个参数的静态方法。这让我们在java调用扩展函数就像调用静态方法一样,假如我们的扩展函数定义在com.dengyin2000.kotlintest1.test.kt
文件中。那么会生成一个com.dengyin2000.kotlintest1.TestKt.class
java类文件,其中就包含了我们在Kotlin中定义的那两个扩展函数。如图:
下面就是在Java中调用Kotlin扩展函数的方法:
package com.dengyin2000.java;
import com.dengyin2000.kotlintest1.Button;
import com.dengyin2000.kotlintest1.TestKt;
import com.dengyin2000.kotlintest1.View;
public class Test {
public static void main(String[] args) {
View view = new Button();
TestKt.longClick(view);
}
}
看到这里你应该也明白了,为什么扩展函数不能覆盖(overriding)了。
2. 扩展属性
扩展属性提供了一种方法用能通过属性语法进行访问的API来扩展。尽管它们被叫做属性,但是它们不能拥有任何状态,它不能添加额外的字段到现有的java对象实例。不过可以有更简短的语法在某些时候还是更方便的。
package com.dengyin2000.kotlintest1
val String.lastChar: Char
get() = get(length - 1)
var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value) {
this.setCharAt(length -1, value)
}
fun main(args: Array) {
println("Kotlin".lastChar)
val sb = StringBuilder("Kotlin")
sb.lastChar = 'g'
println(sb)
}
可以看到扩展属性也可以通过val或者var定义,然后也是接你需要扩展的类型,然后属性名称,最后是属性的类型。var的话可以有set方法定义。你访问扩展属性和访问成员属性完全一样。