Kotlin的扩展属性和扩展方法

作者: 为自己代颜_ | 来源:发表于2018-04-29 23:08 被阅读49次

    扩展方法的原理

    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() }
    

    相关文章

      网友评论

      • Troll4it:var String.s: Int
        get() = this.length
        set(value){
        //set方法并没有field可以用来存储value,
        //其实际作用是使用通过value来操作调用者,即this
        println(this.plus(value))
        }
        你好...对于上面的一段自定义属性并不是很清楚,想请教下。疑问:按照Java的思维,一般给String对象创建一个新的属性 s,一般都是对属性s进行set方法,在通过get方法来取值,按照相同的思路,你上面的代码并没有相同的思维。只是单独的get方法,set方法没有什么用处
      • yask:TestObjectKt这个名字是怎么来的

      本文标题:Kotlin的扩展属性和扩展方法

      本文链接:https://www.haomeiwen.com/subject/gkwalftx.html