Programming Language/Kotlin

[Kotlin] lateinit과 lazy 차이점

개발왕 금골드 2020. 12. 23. 21:19
반응형

목차

  • 소개
  • lateinit
  • lazy
  • lateinit과 lazy의 차이점
    • 초기화 시간
    • Nullable vs Non-nullable
    • Compile-time vs Run-time error
    • 스레드 안정성
  • 결론

 

소개

Kotlin은 전역 변수를 선언하면 반드시 초기화 해줘야 한다. 전역 변수를 초기화하지 않고 선언할 수 있도록 도와주는 키워드가 lateinit과 lazy다. 먼저 Kotlin에 lateinit과 lazy가 있는 이유는 null 안정성 시스템 덕분이다. 속성을 nullable로 선언하면 사용할 때마다 ? 연산자나 !! 연산자를 사용해야 하기 때문에 번거로움이 있고 그렇지 않다면 반드시 초기화를 해줘야 한다. 초기화를 하지 못 하는 상황이 발생할 수도 있기 때문에 Kotlin 언어에서 이 둘을 지원한다.

 

1. lateinit

 

latent 키워드는 null이 아닌 속성 변수를 즉시 초기화하지 않아도 선언 가능하게 도와준다. 이렇게 되면 무거운 초기화 (예 : 서버 통신, DB에서 값 읽어오기 등)가 필요한 속성 값 혹은 객체가 생성되는 동안 수행할 수 없는 Context 작업이 필요할 때 유용하다. 만약 lateinit 키워드가 붙은 변수를 초기화하기 전에 접근한다면 NullPointer Exception, UnInitialized Exception이 발생하게 된다. 현재 lateinit 변수의 초기화를 확인할 수 있는 isInitialized 로 확인한 후 접근하는 것도 좋은 방법이다.

  • var 키워드와 함께 사용할 수 있다. (val 불가능)
  • Java primitive type은 사용할 수 없다. (Kotlin Int, Float 등)
  • Custom getter/setter를 정의할 수 없다.
class Test() {
  lateinit var example: String
  
  fun initializeExample() {
    example = "Example"
  }
  
  fun printExample() {
    if (example.isInitialized) {
      println(example)
    } else {
      println("example is not initialized")
    }
  }
}

 

2. lazy

 

lazy 키워드는 해당 변수에 처음 접근할 때 어떤 계산식과 더불어 변수를 초기화한다. 변수를 lazy 초기화했을 경우 최대 장점은 필요한 경우 변수에 접근해서 초기화하기 때문에 불필요한 리소스 낭비를 하지 않는다는 점이다. 만약 모든 변수를 시작부터 초기화한다면 초기화된 모든 변수가 메모리에 올라간다. 하지만, lazy 초기화를 선언한 변수는 처음 접근한 시점에 메모리에 올라간다.

  • by 키워드를 사용하며 대리자(delegate)를 지정한다.
  • val 키워드와 함꼐 사용한다. (컴파일될 때 final 키워드 적용. var 불가능.)
  • Custom getter/setter를 정의할 수 없다. (val이기 때문에 setter 사용 불가능)
  • 기본적으로 thread에 안전하다. (Synchronized)
class Test() {
  val example: String by lazy {
    // 어떤 계산 식이 코드 블럭 안에 포함될 수 있는데,
    // 이 계산 식 또한 처음 접근할 때 계산된다.
    // 최종적으로 example 변수에 할당되는 값은 마지막 라인이 된다.
    "Example"
  }
}

 

lateinit과 lazy의 차이점

초기화 시간

lateinit은 초기화 시점이 명확하게 정해져 있지 않고, 초기화 순간을 개발자가 정한다. lazy는 처음 접근할 때 초기화된다.

 

Nullable vs Non-nullable

  • lateinit은 Non-nullable로 선언되어야 한다. null을 허용하게 되면 일반 변수를 null로 초기화해서 사용하는 것과 같다.
  • lazy는 Nullable 변수로 선언할 수 있다. 접근하지 않고 초기화되지 않은 상태를 유지하거나 null을 할당해서도 사용할 수 있다.

 

Compile-time vs Run-time error

lateinit은 초기화되어 있지 않았을 때 접근하면 Exception이 발생한다. 반면, lazy 초기화는 컴파일 때 초기화에 대한 검사를 진행한다. 만약 lazy 초기화가 제대로 이루어지지 않았다면 에러가 발생하여 이를 컴파일 시간에 알 수 있다.

 

스레드 안정성

lazy 변수는 스레드 안정성을 Kotlin 언어에서 지원한다. 기본적으로 다수의 스레드 환경에서 lazy 변수에 접근할 때, 하나의 스레드에서 lazy 델리게이트를 실행하고 초기화를 완료한 후 순차적으로 초기화가 완료된 lazy 변수에 접근되도록 한다.

 

결론

이론적으로 lateinit과 lazy를 이해하는 것도 중요하지만, 직접 써보지 않으면 어떤 때에 무엇을 써야 하는지 감이 잘 오지 않을 수 있다. 직접 사용해보면서 혹은 직접 코딩한 프로젝트 안에서 어떤 때에 무엇을 써야하는지 아는 것이 중요하다고 생각한다.

 


참고자료 : 빅 너드 랜치의 코틀린 프로그래밍 제이펍

stackoverflow.com/questions/36623177/kotlin-property-initialization-using-by-lazy-vs-lateinit

 

Property initialization using "by lazy" vs. "lateinit"

In Kotlin, if you don't want to initialize a class property inside the constructor or in the top of the class body, you have basically these two options (from the language reference): Lazy

stackoverflow.com

https://alessandrofarandagancio.medium.com/exploring-differences-between-lateinit-and-lazy-in-kotlin-518cd329ca6e

 

Exploring Differences Between lateinit and lazy in Kotlin

As an Android engineer with a decade of professional experience, I have witnessed the evolution of the Kotlin programming language and its…

alessandrofarandagancio.medium.com

 

반응형