Kotlin 扩展
在 Java 中当一个类的功能不能满足我们的需求时,我们一般都要通过继承或者使用装饰者模式来对其功能进行扩展。当这种情况发生在 Kotlin 中时,我们完全不用那么麻烦,在 Kotlin 中,扩展一个类的功能时无需继承该类或者使用像装饰者模式,就能达到目的。
给一个类添加扩展函数
现在我们个 IntArray
添加一个扩展函数,用于交换数组中特定下标的两个元素的值。
// 给IntArray添加扩展函数
fun IntArray.swap(oneIndex: Int, otherIndex: Int) {
// “this” 对应的就是当前的Int数组
this[oneIndex] = this[oneIndex] + this[otherIndex]
this[otherIndex] = this[oneIndex] - this[otherIndex]
this[oneIndex] = this[oneIndex] - this[otherIndex]
}
fun main() {
val array = intArrayOf(1, 2, 3, 4, 5)
array.swap(0,4)
array.forEach { print("$it ") }
}
当我们需要对数组添加扩展函数,可以将类型泛化:
fun <T> Array<T>.swap(oneIndex: Int, otherIndex: Int) {
val tmp = this[oneIndex]
this[oneIndex] = this[otherIndex]
this[otherIndex] = tmp
}
fun main() {
val array = arrayOf<Int>(1, 2, 3, 4, 5)
array.swap(0, 4)
array.forEach { print("$it ") }
}
扩展是静态的
扩展是不会在真正的修改它所要扩展的类。 仅仅是通过该类的实例生成了一个新的方法而已。我们将上面的扩展函数转换成 Java 代码:
public static final void swap(@NotNull Object[] $this$swap, int oneIndex, int otherIndex) {
Object tmp = $this$swap[oneIndex];
$this$swap[oneIndex] = $this$swap[otherIndex];
$this$swap[otherIndex] = tmp;
}
可以很清楚的看到,Java 生成了一个静态方法,传入了一个Object的数组对象和 swap()
函数需要的两个参数。扩展函数的作用域取决的是传入的扩展的类型。
open class Shape
class Rectangle : Shape()
// 给 Shape 添加扩展函数,就是Shape的,并不会动态的分发给它的子类
fun Shape.getName() = "Shape"
// 给 Rectangle 添加扩展函数,就是 Rectangle 的
fun Rectangle.getName() = "Rectangle"
// 调用的扩展函数只取决于参数的类型,这里只会调用 Shape 的扩展函数
fun printClassName(s: Shape) {
println(s.getName())
}
fun main() {
printClassName(Rectangle())
}
上面的代码输出结果为:
Shape
根据上面的例子,可以说明:扩展函数只会作用在扩展的本类上,不会作用在其子类上。
如果要扩展的类已存在的函数,则在调用时候,并不会调用扩展函数 ,前面提到扩展并不会改变类的内容,而根据转而来的 Java 代码可以到看到,扩展函数其实是静态的,并不需要实例去调用,如果我们通过实例调用,只会调用类中已经存在的方法。例如:
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") }
Example().printFunctionType(1)
上面的代码输出:
Extension function
扩展属性
Kotlin 不止支持扩展函数,同样也支持扩展属性。由于扩展并不能改变要扩展的类,所以扩展是不支持 幕后字段的。所以扩展属性不能在声明的时候初始化,必须通过 setter/getter
来定义。
open class Shape
class House {
// 给 Shape 扩展一个 name 属性
val Shape.name: String
get() {
return "ShapeName"
}
fun printName() {
val shape = Shape()
println(shape.name)
}
}
fun main() {
val house = House()
house.printName()
}
伴生对象的扩展
伴生对象的扩展类似扩展函数,至于要在伴生对象后面跟点然后再跟要扩展的方法就可以了,如下:
class MyClass {
companion object { } // 将被称为 "Companion"
}
fun MyClass.Companion.printCompanion() { println("companion") }
fun main() {
MyClass.printCompanion()
}
上面的代码给伴生对象扩展出了一个函数 printCompanion()
,调用类似伴生对象的调用。