devlog of ShinJe Kim

[TIL] 2019-10-16

|

Today I Learned

  • kotlin에서 lateinit varby lazy {...}의 차이점은? (출처: stackoverflow - kotlin-property-initialization-using-by-lazy-vs-lateinit)
    • lazy { ... } 위임자(delegate)는 val 프로퍼티에만 쓰일 수 있는 반면 lateinitfinal 필드에 컴파일될 수 없으며 불변성을 보장할 수 없으므로 var에만 쓰일 수 있습니다.
    • lateinit var는 값을 저장하는 backing fields를 가지고 있습니다. by lazy { ... }는 값이 계산되어 저장되는 곳에 위임 객체(delegate object)를 생성하며 클래스 객체 내부의 위임자 인스턴스(delegate instance)에 대한 참조를 저장하고, 위임자 인스턴스와 함께 수행되는 프로퍼티의 getter를 생성합니다. 따라서 클래스에 backing field가 필요하면 lateinit를 쓰세요.
    • val과 더불어 lateinit은 널을 참조할 수 있는 프로퍼티나 자바의 기본 타입에 쓰일 수 없습니다. null은 초기화되지 않은 값에 쓰이기 때문입니다.
    • lateinit var은 객체가 보이는 곳 어디에서나 초기화할 수 있습니다(예: 프레임워크의 코드 내부). 또한 단일 클래스의 서로 다른 객체에 대한 다중 초기화(multiple initialization)가 가능합니다. 반대로 by lazy { ... }는 서브클래스의 프로퍼티를 오버라이딩 함으로써만 바뀔 수 있는 프로퍼티의 유일한 초기화를 제공합니다. 만약 미리 알 수 없는 프로퍼티를 외부에서 초기화하고 싶다면 lateinit을 사용하세요.
    • by lazy{ ... }를 사용한 초기화는 기본적으로 스레드에 안전하고 initializer가 최대 한 번만 호출되도록 보장해줍니다(하지만 이는 다른 lazy를 오버로딩)을 함으로써 바꿀 수 있습니다). lateinit var의 경우, 멀티 스레드 환경에서 프로퍼티를 올바르게 초기화하는지의 여부는 개발자의 코드에 달려있습니다.
    • Lazy 인스턴스는 저장되고 전달될 수 있으며 다수의 프로퍼티에 사용될 수도 있습니다. 이와 반대로, lateinit var는 그 어떤 런타임 상태도 저장하지 않습니다(초기화되지 않은 값의 필드에만 null을 할당합니다).
    • Lazy 인스턴스에 대한 참조를 가지고 있으면, isInitialized()는 해당 인스턴스가 이미 초기화되었는지 아닌지를 체크할 수 있도록 합니다(그리고 당신은 위임된 프로퍼티에서 반영하여 이러한 인스턴스를 얻을 수 있습니다). lateinit 프로퍼티가 초기화되었는지 아닌지 확인하려면 코틀린 1.2부터 지원되는 property::isInitialized를 사용하면 됩니다.
    • by lazy{ ... }를 통해 전달되는 람다는 클로저에 의해 사용되는 context에서 참조를 가져올 수 있습니다. 그런다음 해당 참조를 저장하고, 프로퍼티가 초기화 되는 경우에만 release합니다. 이로 인해 안드로이드 액티비티와 같은 객체의 계층구조가 너무 오랫동안 release되지 않거나 혹은 아예 되지 않는 상황이 발생할 수 있습니다. 따라서 initializer 람다 내부에서 사용하는 것을 주의해야합니다.
  • 이펙티브 자바 아이템 83. “지연 초기화는 신중히 사용하라”
    • 지연 초기화는 양날의 검이다. 클래스 혹은 인스턴스 생성 시의 초기화 비용은 줄지만 그 대신 지연 초기화하는 필드에 접근하는 비용은 커진다. 지연 초기화하려는 필드들 중 결국 초기화가 이뤄지는 비율에 따라, 실제 초기화에 드는 비용에 따라, 초기화된 각 필드를 얼마나 빈번히 호출하느냐에 따라 지연 초기화가(다른 수많은 최적화와 마찬가지로) 실제로는 성능을 느려지게 할 수도 있다.

    • 멀티스레드 환경에서는 지연 초기화를 하기가 까다롭다.

    • 그럼에도 지연 초기화가 필요할 때는?

      해당 클래스의 인스턴스 중 그 필드를 사용하는 인스턴스의 비율이 낮은 반면, 그 필드를 초기화하는 비용이 클 때. 이 떄에는 지연 초기화가 제 역할을 해 줄 것이다. (하지만 안타깝게도 정말 그런지를 알 수 있는 유일한 방법은 지연 초기화 적용 전후의 성능을 측정해보는 것이다.)

    • 대부분의 상황에서 일반적인 초기화가 지연 초기화보다 낫다.
    • 성능 때문에 혹은 위험한 초기화 순환을 막기 위해 꼭 지연 초기화를 써야 한다면 올바른 지연 초기화 기법을 사용하자. 인스턴스 필드에는 이중검사 관용구를, 정적 필드에는 지연 초기화 홀더 클래스 관용구를 사용하자. 반복해 조기화해도 괜찮은 인스턴스 필드에는 단일검사 관용구도 고려 대상이다.

Comments