Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[아이템 21] 델리게이터 사용 예시 #255

Open
NDjust opened this issue May 3, 2022 · 1 comment
Open

[아이템 21] 델리게이터 사용 예시 #255

NDjust opened this issue May 3, 2022 · 1 comment
Assignees

Comments

@NDjust
Copy link

NDjust commented May 3, 2022

p.129 코틀린 stdlib 에서 다음과 같은 프로퍼티 렐리게이터를 알아 두면 좋습니다.

질문 :
책에서 나오는 아래 프로퍼티 델리게이터 패턴에 대한 예시와 어떻게 동작하는지 설명해주시면 좋을 거 같습니다.

  • lazy
  • Delegates.observable
  • Delegates.vetoable
  • Delegates.notNull
@Gobukgol
Copy link
Contributor

observable과 vetoable을 먼저 보면 좋을 것 같아.
observable과 vetoable은 간단하게 프로퍼티의 변화를 감지하고 정의한 동작을 한다. 라 볼 수 있는데 vetoable의 경우 정의한 동작에 boolean 타입의 반환값이 존재해.
vetoable의 경우 반환된 boolean의 값이 true 일 경우에만 최종적으로 프로퍼티의 값을 변경시켜.

class DelegatesExample {
    var observable : Int by Delegates.observable(0) { _, oldValue, newValue ->
        println("oldValue = $oldValue, newValue = $newValue ")
    }

    var vetoable : Int by Delegates.vetoable(0) { _, oldValue, newValue ->
        println("oldValue = $oldValue, newValue = $newValue ")
        oldValue < newValue
    }

    fun observableFunc() {
        println("observable delegates")
        println("observable = $observable")
        observable = -1
        println("observable = $observable")
    }

    fun vetoableFunc() {
        println("vetoable delegates")
        println("vetoable = $vetoable")
        vetoable = -1
        println("vetoable = $vetoable")
        vetoable = 1
        println("vetoable = $vetoable")
    }
...
}

fun main() {
    val delegates = DelegatesExample()
...
    delegates.observableFunc()
    delegates.vetoableFunc()
...
}

이 코드의 결과는 아래와 같아

image

다음으로 lazy와 Delegates.notNull을 보면 좋을 것 같아.
lazy와 Delegates.notNull에 대해 얘기하기전에 lateinit 의 특징에 대해서 간단하게 보고 넘어가자면

  • 선언은 해두고 나중에 초기화하여 사용
  • var 에만 붙일 수 있다. 즉 초기화 후에 변경이 가능하다
  • primitive 타입에는 사용 불가
  • nullable 할 수 없다. lateinit var a : String? 불가
  • 초기화하지 않고 사용시 exception 발생
class Socks {
    lateinit var size: Size

    fun init(size: Int) {
        this.size = Size(size)
    }
}

class Size(val size: Int)

위 코드는 아래와 같이 컴파일 돼.

public final class Socks {
   public Size size;

   @NotNull
   public final Size getSize() {
      Size var10000 = this.size;
      if (var10000 == null) {
         Intrinsics.throwUninitializedPropertyAccessException("size");
      }

      return var10000;
   }

   public final void setSize(@NotNull Size var1) {
      Intrinsics.checkNotNullParameter(var1, "<set-?>");
      this.size = var1;
   }

   public final void init(int size) {
      this.size = new Size(size);
   }
}

코드를 보면 생성할 때 Size 클래스는 null로 초기화 되어있고, getSize를 할 때 null 체크를 하는걸 알 수 있어. null로 초기화해야하기 때문에 primitive 타입은 사용할 수 없고, 값을 가져올 때마다 null 체크 후 exception을 던지기 때문에 nullable 타입에는 사용할 수 없는거지

다음으로 lateinit과 비교했을때 lazy의 특징은 아래와 같아.

  • 선언할 때 초기화 코드를 작성해야함
  • primitive 타입 사용 가능
  • val 에 사용
  • nullable 타입도 사용 가능

아래와 같은 코드가 있을 때 자바로 컴파일 해보면

class LazyValue {
    val size: Int by lazy {
        println("hi! lazy")
        1000
    }
}
public final class LazyValue {
   @NotNull
   private final Lazy size$delegate;

   public final int getSize() {
      Lazy var1 = this.size$delegate;
      Object var3 = null;
      return ((Number)var1.getValue()).intValue();
   }

   public LazyValue() {
      this.size$delegate = LazyKt.lazy((Function0)null.INSTANCE);
   }
}

다음으로 Delegates.notNull의 특징을 보면

  • primitive 타입도 사용 가능
  • 초기화 전에 사용하면 exception 발생
  • null을 할당할 수 없다

아래와 같은 코드가 있을 때 자바로 컴파일 해보면

class DelegatesNotNull {
    var size: Int by Delegates.notNull()

    fun init() {
        println(size)
    }
}
public final class DelegatesNotNull {
   // $FF: synthetic field
   static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(DelegatesNotNull.class, "size", "getSize()I", 0))};
   @NotNull
   private final ReadWriteProperty size$delegate;

   public final int getSize() {
      return ((Number)this.size$delegate.getValue(this, $$delegatedProperties[0])).intValue();
   }

   public final void setSize(int var1) {
      this.size$delegate.setValue(this, $$delegatedProperties[0], var1);
   }

   public final void init() {
      int var1 = this.getSize();
      System.out.println(var1);
   }

   public DelegatesNotNull() {
      this.size$delegate = Delegates.INSTANCE.notNull();
   }
}

lazy와 Delegates.notNull의 경우 lateinit 과 다르게 Lazy 또는 ReadWriteProperty로 한번 wrapping하고 사용하기 때문에 primitive 타입도 사용 가능한 것을 확인할 수 있어.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants