扩展方法的原理
Kotlin 中类的扩展方法并不是在原类的内部进行拓展,通过反编译为Java代码,可以发现,其原理是使用装饰模式,对源类实例的操作和包装,其实际相当于我们在 Java中定义的工具类方法,并且该工具类方法是使用调用者为第一个参数的,然后在工具方法中操作该调用者;
该调用者在Kotlin中使用this关键字表示;
比如:定义一个String的扩展方法,其中的this表示调用者本身;
fun String.times(t:Int){
val sb = StringBuilder()
for (i in 0 until t) {
sb.append(this)
}
println(sb.toString())
}
Kotlin中的调用方式: “aaaa”.times(10)
反编译为对应的Java代码:
public final class TestObjectKt {
public static final void times(@NotNull String $receiver, int t) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
StringBuilder sb = new StringBuilder();
IntRange var10000 = RangesKt.until(0, t);
int i = var10000.getFirst();
int var4 = var10000.getLast();
if(i <= var4) {
while(true) {
sb.append($receiver);
if(i == var4) {
break;
}
++i;
}
}
String var5 = sb.toString();
System.out.println(var5);
}
}
Java中的调用方式: TestObjectKt.times(“aaaa”,10);
可见Kotlin中实际是将调用者”aaaa”作为方法times的第一个参数;
扩展属性的原理
类的扩展属性原理其实与扩展方法是一样的,只是定义的形式不同,扩展属性必须定义get和set方法,并且类似于接口中定义的变量,没有 backingfield,即没有field关键字,不能用来存储变量。(一般的类属性,在其对象实例中都会分配一点内存来存储属性的值。)
fun main(args: Array) {
val str = "aa"
//没有backing field,不能存储值,其实际是通过setXXX(str,10)操作str
//输出:aa10
str.s = 10
//输出:2
println(str.s)
}
var String.s: Int
get() = this.length
set(value){
//set方法并没有field可以用来存储value,
//其实际作用是使用通过value来操作调用者,即this
println(this.plus(value))
}
对应的Java代码:
public final class ExtendsKt {
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
String str = "aa";
setS(str, 10);
int var2 = getS(str);
System.out.println(var2);
}
public static final int getS(@NotNull String $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return $receiver.length();
}
public static final void setS(@NotNull String $receiver, int value) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
String var2 = $receiver + value;
System.out.println(var2);
}
}
可以看出,为什么扩展属性会没有backing field,其实际仍然是工具方法,并不是在原类内部扩展。
扩展方法实现迭代遍历
实现了Iterable接口的类,可以使用for循环进行遍历;
通过扩展方法,类无需实现Iterator接口,即可实现被迭代;
fun ViewGroup.children() = object : Iterable {
override fun iterator() = object : Iterator {
var index = 0
override fun hasNext() = index < childCount
override fun next() = getChildAt(index++)
}
}
val views = // ...
for (view in views.children()) {
// TODO do something with view
}
val visibleHeight = views.children()
.filter { it.visibility == View.VISIBLE }
.sumBy { it.measuredHeight }
扩展ViewGroup属性获取ChildView
val ViewGroup.children: List
get() = (0..childCount -1).map { getChildAt(it) }
parent.children.forEach { it.visible() }