Kotlin 扩展

在 Java 中当一个类的功能不能满足我们的需求时,我们一般都要通过继承或者使用装饰者模式来对其功能进行扩展。当这种情况发生在 Kotlin 中时,我们完全不用那么麻烦,在 Kotlin 中,扩展一个类的功能时无需继承该类或者使用像装饰者模式,就能达到目的。

给一个类添加扩展函数

现在我们个 IntArray 添加一个扩展函数,用于交换数组中特定下标的两个元素的值。

  1. // 给IntArray添加扩展函数
  2. fun IntArray.swap(oneIndex: Int, otherIndex: Int) {
  3. // “this” 对应的就是当前的Int数组
  4. this[oneIndex] = this[oneIndex] + this[otherIndex]
  5. this[otherIndex] = this[oneIndex] - this[otherIndex]
  6. this[oneIndex] = this[oneIndex] - this[otherIndex]
  7. }
  8. fun main() {
  9. val array = intArrayOf(1, 2, 3, 4, 5)
  10. array.swap(0,4)
  11. array.forEach { print("$it ") }
  12. }

当我们需要对数组添加扩展函数,可以将类型泛化:

  1. fun <T> Array<T>.swap(oneIndex: Int, otherIndex: Int) {
  2. val tmp = this[oneIndex]
  3. this[oneIndex] = this[otherIndex]
  4. this[otherIndex] = tmp
  5. }
  6. fun main() {
  7. val array = arrayOf<Int>(1, 2, 3, 4, 5)
  8. array.swap(0, 4)
  9. array.forEach { print("$it ") }
  10. }

扩展是静态的

扩展是不会在真正的修改它所要扩展的类。 仅仅是通过该类的实例生成了一个新的方法而已。我们将上面的扩展函数转换成 Java 代码:

  1. public static final void swap(@NotNull Object[] $this$swap, int oneIndex, int otherIndex) {
  2. Object tmp = $this$swap[oneIndex];
  3. $this$swap[oneIndex] = $this$swap[otherIndex];
  4. $this$swap[otherIndex] = tmp;
  5. }

可以很清楚的看到,Java 生成了一个静态方法,传入了一个Object的数组对象和 swap()函数需要的两个参数。扩展函数的作用域取决的是传入的扩展的类型。

  1. open class Shape
  2. class Rectangle : Shape()
  3. // 给 Shape 添加扩展函数,就是Shape的,并不会动态的分发给它的子类
  4. fun Shape.getName() = "Shape"
  5. // 给 Rectangle 添加扩展函数,就是 Rectangle 的
  6. fun Rectangle.getName() = "Rectangle"
  7. // 调用的扩展函数只取决于参数的类型,这里只会调用 Shape 的扩展函数
  8. fun printClassName(s: Shape) {
  9. println(s.getName())
  10. }
  11. fun main() {
  12. printClassName(Rectangle())
  13. }

上面的代码输出结果为:

  1. Shape

根据上面的例子,可以说明:扩展函数只会作用在扩展的本类上,不会作用在其子类上

如果要扩展的类已存在的函数,则在调用时候,并不会调用扩展函数 ,前面提到扩展并不会改变类的内容,而根据转而来的 Java 代码可以到看到,扩展函数其实是静态的,并不需要实例去调用,如果我们通过实例调用,只会调用类中已经存在的方法。例如:

  1. class Example {
  2. fun printFunctionType() { println("Class method") }
  3. }
  4. fun Example.printFunctionType() { println("Extension function") }
  5. Example().printFunctionType()

上面代码输出:

  1. Class method

当然,扩展函数重载同样名字但不同签名成员函数就是完全可以的:

  1. class Example {
  2. fun printFunctionType() { println("Class method") }
  3. }
  4. fun Example.printFunctionType(i: Int) { println("Extension function") }
  5. Example().printFunctionType(1)

上面的代码输出:

  1. Extension function

扩展属性

Kotlin 不止支持扩展函数,同样也支持扩展属性。由于扩展并不能改变要扩展的类,所以扩展是不支持 幕后字段的。所以扩展属性不能在声明的时候初始化,必须通过 setter/getter 来定义。

  1. open class Shape
  2. class House {
  3. // 给 Shape 扩展一个 name 属性
  4. val Shape.name: String
  5. get() {
  6. return "ShapeName"
  7. }
  8. fun printName() {
  9. val shape = Shape()
  10. println(shape.name)
  11. }
  12. }
  13. fun main() {
  14. val house = House()
  15. house.printName()
  16. }

伴生对象的扩展

伴生对象的扩展类似扩展函数,至于要在伴生对象后面跟点然后再跟要扩展的方法就可以了,如下:

  1. class MyClass {
  2. companion object { } // 将被称为 "Companion"
  3. }
  4. fun MyClass.Companion.printCompanion() { println("companion") }
  5. fun main() {
  6. MyClass.printCompanion()
  7. }

上面的代码给伴生对象扩展出了一个函数 printCompanion() ,调用类似伴生对象的调用。

扩展作用域