Kotlin的扩展属性和扩展方法

扩展方法的原理

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<String>) {
    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<View> {
 override fun iterator() = object : Iterator<View> {
   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() }
    原文作者:黑暗世界的微光
    原文地址: https://www.jianshu.com/p/b802c5767b91
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞