Kotlinの拡張プロパティと拡張方法

6278 ワード

拡張方法の原理
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での呼び出し方式:「aaaaa」.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 }

拡張View GroupプロパティChildViewの取得
val ViewGroup.children: List
    get() = (0..childCount -1).map { getChildAt(it) }

parent.children.forEach { it.visible() }