kotlin-korea / Study-Log

스터디 로그 및 기타 자료
MIT License
62 stars 4 forks source link

[Note] Delegation & `by` #25

Closed cwdoh closed 7 years ago

cwdoh commented 7 years ago

23 에서 궁금했던 by에 대해 역시 의식의 흐름대로...

Class Delegation

warmup

간단하게 interface를 정의하고 이를 기반으로 코드를 구현

interface Base {
    fun printX()
}

class BaseImpl(val x: Int) : Base {
    override fun printX() { print(x) }
}
public interface Base {
   void printX();
}

public final class BaseImpl implements Base {
   private final int x;

   public void printX() {
      int var1 = this.x;
      System.out.print(var1);
   }

   public final int getX() { return this.x; }
   public BaseImpl(int x) { this.x = x; }
}

아주 당연한 결과다.

by에 의한 메소드 생성

문서에 보면 아래와 같은 내용이 있다.

The by-clause in the supertype list for Derived indicates that b will be stored internally in objects of Derived and the compiler will generate all the methods of Base that forward to b.

요약하면,

class Derived(baseImpl: Base) : Base by baseImpl

위의 코드는 아래와 같이 구현된다.

public final class Derived implements Base {
   // $FF: synthetic field
   private final Base $$delegate_0;

   public Derived(@NotNull Base baseImpl) {
      Intrinsics.checkParameterIsNotNull(baseImpl, "baseImpl");
      super();
      this.$$delegate_0 = baseImpl;
   }

   public void printX() {
      this.$$delegate_0.printX();
   }
}

interface에 없는 메소드를 구현하면?

당연하겠지만 interface에서 정의하지 않은 것은 위임의 대상이 되지 않겠지..

interface Base {
    fun printX()
}

class BaseImpl(val x: Int) : Base {
    override fun printX() { print(x) }

    private var y : Int = 10
    fun printY() { print(y) }
}

생성된 코드를 보면 이전과 같다.

public interface Base {
   void printX();
}

public final class BaseImpl implements Base {
   // ...
}

public final class Derived implements Base {
   // $FF: synthetic field
   private final BaseImpl $$delegate_0;

   public Derived(@NotNull Base baseImpl) {
      Intrinsics.checkParameterIsNotNull(baseImpl, "baseImpl");
      super();
      this.$$delegate_0 = new BaseImpl(10);
   }

   public void printX() {
      this.$$delegate_0.printX();
   }
}

Delegated properties

문법 val/var <property name>: <Type> by <expression>

The expression after by is the delegate, because get() (and set()) corresponding to the property will be delegated to its getValue() and setValue() methods. Property delegates don’t have to implement any interface, but they have to provide a getValue() function (and setValue() — for var's).

요약하면, prop의 get와 set을 getValue()와 setValue()로 위임한다. 인터페이스가 필요없다.

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "Anonymous"
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
    }
}

class Profile {
    var myName: String by Delegate()
}
public final class Delegate {
   @NotNull
   public final String getValue(@Nullable Object thisRef, @NotNull KProperty property) {
      Intrinsics.checkParameterIsNotNull(property, "property");
      return "Anonymous";
   }

   public final void setValue(@Nullable Object thisRef, @NotNull KProperty property, @NotNull String value) {
      Intrinsics.checkParameterIsNotNull(property, "property");
      Intrinsics.checkParameterIsNotNull(value, "value");
   }
}

public final class Profile {
   @NotNull
   private final Delegate myName$delegate = new Delegate();
   // $FF: synthetic field
   static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Profile.class), "myName", "getMyName()Ljava/lang/String;"))};

   @NotNull
   public final String getMyName() {
      return this.myName$delegate.getValue(this, $$delegatedProperties[0]);
   }

   public final void setMyName(@NotNull String var1) {
      Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
      this.myName$delegate.setValue(this, $$delegatedProperties[0], var1);
   }
}

23 에서 확인했던 lazy에서의 내부구조와 크게 다를 것은 없다.

cwdoh commented 7 years ago

TODO: class delegation의 다른 형태가 뭔가 있을 수도 있겠다. 고민해보자

elvin-han commented 7 years ago

글 잘 보고있습니다.^^

Class delegation에서 요약해주신 글이요. 원문과 요약문이 잘 매칭이 안되서요..

[as-is]

아래의 형태가 맞지 않을까요? A = Base, B = BaseImpl, b = BaseImpl의 object, C = Drived 라고 이해했습니다.

[to-be]

cwdoh commented 7 years ago

@elvin-han 말씀하신 바가 맞습니다. 제가 설명을 수정하던 중에 알파벳을 잘못 바꿨군요. ㅜ C 내의 private member로 b가 저장되고, A 메소드는 이를 참조하는 형태로 C에 동일하게 생성되는 것이 맞습니다.