@[toc]
扩展函数给本来单薄的类添加了许多功能,今天我们来详细看一下扩展函数和扩展属性到底是什么,应该怎么用。
扩展函数和扩展属性的实现
我们都知道,Java 中,只有一个类型的成员属性和成员方法才能用“对象.属性 / 方法()”的方式调用,一个类型的对象是绝对不可能通过这种方法调用其他类里定义的方法(除非存在继承或实现关系)。而 Kotlin 提供的扩展函数和扩展属性打破了这一规则,它是怎么实现的呢?
首先看例子:
// Test.kt
fun <T> MutableList<T>.swap(indexA: Int, indexB: Int) {
val temp = this[indexA]
this[indexA] = this[indexB]
this[indexB] = temp
}
val Int.isOdd: Boolean
get() = this and 1 == 1
我们在这里定义一个扩展函数 swap,它的接收者是 MutableList,作用是调换传入的两个索引对应的值。然后给 Int 类定义了一个扩展属性 isOdd,用来检查这个 Int 是不是奇数。
这时我们就可以这样调用它们了:
val list = mutableListOf(1, 2, 3)
list.swap(0, 1)
println(list)
// [2, 1, 3]
val n = 3
println(n.isOdd)
// true
但想要在 Java 中调用它们,就要这么写了:
// import TestKt
List<Integer> list = new ArrayList<>();
list.add(1); list.add(2); list.add(3);
TestKt.swap(list, 0, 1);
System.out.println(list);
int n = 3;
println(TestKt.isOdd(n));
实际上,所有的扩展函数和扩展属性都会被编译成一个方法,这个方法的第一个参数就是扩展的接收者,然后才是其它各个参数。对于扩展属性来说 ,因为编译后这个属性并不存在,所以不能像一般的类属性那样对它进行初始化,而是要自定义 getter 和 setter 来访问它。
为什么要用扩展函数和扩展属性
Java 里有许多工具类,比如 Collections、Arrays、Objects 等等,它们提供了一系列静态方法来充当工具函数,通过参数传入被操作的对象,既不直观又冗长无比。
比如对于 Integer.parseInt(String s),Kotlin 就用一个扩展函数替代了它:
inline fun String.toInt() = java.lang.Integer.parseInt(this)
虽然还是调用这个方法,但这样定义有两个好处,一是减少了代码量,二是形成了一个统一的标准,所有其他基本类型都可以重载这个方法,实现同一个行为。
从另一个角度来看,Kotlin 鼓励开发者 尽量精简类的定义,一个类只定义框架,工具函数可以通过外部扩展一点点地添加,尽量不改动原有的类。
网友评论