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

[Chapter - 08] lateinit과 Delegates.notNull 의 차이 #5

Closed
sawooook opened this issue Jul 5, 2023 · 4 comments
Closed

[Chapter - 08] lateinit과 Delegates.notNull 의 차이 #5

sawooook opened this issue Jul 5, 2023 · 4 comments
Assignees
Labels
Question✋ 질문 하기 라벨!

Comments

@sawooook
Copy link
Contributor

sawooook commented Jul 5, 2023

질문 내용을 적어주세요 📝

60p 반대로 lateinit을 사용할 수 없는 경우도 있습니다 JVM에서 int, long, double, boolean과 같은 ...

이부분이 있는데 왜 lateinit은 불가능하고, Delegates.notNull 은 가능할까요?!
둘이 어떤 차이가 있는지 궁금합니다!

@sawooook sawooook added the Question✋ 질문 하기 라벨! label Jul 5, 2023
@JungTag
Copy link

JungTag commented Jul 5, 2023

@sawooook
궁금한 게 있는데, 요거 답변은 어떤 방식으로 작성하면 될까요??

  1. 여기다가 바로 달기
  2. 스터디 시간에 답변하기
  3. 스터디 시간에 답변한 후 여기다가 정리해서 달기

@sawooook
Copy link
Contributor Author

sawooook commented Jul 5, 2023

@JungTag 바로 달까영??! 그리고 스터디때 답변 단거 정리해주시는건 어떠세용~?

@JungTag
Copy link

JungTag commented Jul 5, 2023

@sawooook 좋습니당~

@JungTag
Copy link

JungTag commented Jul 5, 2023

1. 원시 타입이 lateinit에서는 사용이 불가능한 반면, Delegates.notNull에서 사용이 가능한 이유?

먼저 lateinit 내부 구현을 살펴봅시다.

class Sample {
    lateinit var prop: Prop

    fun init(prop: Prop) {
        this.prop = prop
    }
}

class Prop(val value: Int)

fun main() {
    val sample = Sample()
    sample.init(Prop(10))
    println(sample.prop.value)
}

이를 디컴파일해보면 다음과 같습니다.

public final class Sample {
   public Prop prop; // 처음엔 null, 추후 setter에 의해 초기화

   @NotNull
   public final Prop getProp() {
      Prop var10000 = this.prop;
      if (var10000 == null) {
         Intrinsics.throwUninitializedPropertyAccessException("prop");
      }

      return var10000;
   }

   public final void setProp(@NotNull Prop var1) {
      Intrinsics.checkNotNullParameter(var1, "<set-?>");
      this.prop = var1;
   }

lateinit은 선언한 타입과 동일한 객체를 그대로 사용합니다.
이때, 프로퍼티가 null로 초기화되므로 원시타입은 사용이 불가능합니다.

그러면 java에서 사용되는 Wrapper 클래스(Integer, Long, Boolean 등)를 넣어주면 되는 거 아니야?라고 생각하실 수 있습니다.

코틀린에서 기본 자료형을 사용하면 기본적으로 자바의 원시 타입으로 컴파일 되고, nullable 타입으로 선언하거나 제네릭 타입으로 사용할 때에 경우 Wrapper 클래스로 컴파일 됩니다.

lateinit은 제네릭 타입을 사용하지 않고(선언한 타입 그대로 사용), @NotNull 애너테이션에 의해 nullable한 값을 set할 수 없기 때문에 이러한 원시 자료형의 Wrapper 클래스 또한 사용이 불가능합니다.

그 다음으로 Delegates.notNull()의 내부구현을 살펴봅시다.

public object Delegates {

    public fun <T : Any> notNull(): ReadWriteProperty<Any?, T> = NotNullVar()
    ...
}

private class NotNullVar<T : Any>() : ReadWriteProperty<Any?, T> {
    private var value: T? = null

    public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
    }

    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        this.value = value
    }
}

Delegates.notNull()은 lateinit과 달리 타입을 그대로 쓰는 게 아니라 NotNullVar이라는 객체에 래핑하여 사용합니다.
이때 제네릭 타입으로 선언한 타입이 사용되기 때문에 컴파일 과정에서 Wrapper 클래스로 치환되므로 원시 타입 사용이 가능한 것입니다.

2. 둘의 차이 & 공통점 정리

먼저 둘의 차이점을 정리하자면 다음과 같습니다.

  • lateinit은 생성 비용이 적은 반면, Delegates.notNull은 객체를 한 번 감싸므로 생성 비용이 크다
  • lateinit은 원시 타입에 사용할 수 없지만, Delegates.notNull은 사용할 수 있다
  • Delegates.notNull은 의존성 주입 기술을 통해 초기화 하는 게 어려울 수 있다 (래핑 타입을 쓰므로)
    image

공통점은 다음과 같습니다.

  • nullable한 타입으로 선언할 수 없다
  • 초기화 하지 않고 값을 사용하면 에러가 발생한다 (예외의 종류는 다르다)
  • 둘 다 custom getter/setter를 사용할 수 없다

ref) https://stackoverflow.com/questions/44205389/difference-between-delegates-notnull-and-lateinit-kotlin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question✋ 질문 하기 라벨!
Projects
None yet
Development

No branches or pull requests

2 participants