Kotlin 지연 초기화 방법 정리

패스트 캠퍼스 실무 프로젝트로 배우는 Kotlin & Spring 강의 정리

지연 초기화

  • 대상에 대한 초기화를 미뤘다가 실제 사용 시점에 초기화하는 방법
  • 초기화 과정에서 자원을 많이 쓰거나 오버헤드가 발생할 경우 유리할 수 있음
  • 예제
    • 무한 스크롤
    • 싱글톤 패턴의 지연 초기화
    • JPA의 엔티티 lazy loading
  • 코틀린에서는 두 가지 방식의 지연 초기화 제공
  • by lazy
    • 불변 속성에서만 사용 가능
    • 사용 시점에 한 번만 초기화 로직 실행
    • 멀티 스레드 환경에서도 안전하게 동작
    • 내부에 synchronized 블록으로 구현되어 있음
    • LazyThreadSafetyMode 멀티 스레드에서 어떻게 lazy 인스턴스 초기화를 동기화할지 설정 가능
      • LazyThreadSafetyMode.NONE : 멀티 스레드 환경이 아닌 경우, 동기화 작업이 오버헤드가 될 수 있으므로 스레드 세이프 모드 해제 가능
      • LazyThreadSafetyMode.PUBLICATION : 초기화되지 않은 lazy 인스턴스를 동시에 접근할 때는 여러번 초기화 함수를 호출할 수 있지만, 처음 반환된 lazy 인스턴스의 값만 사용
      • LazyThreadSafetyMode.SYNCHORINIZED : 기본 설정. 잠금이 단일 스레드만 lazy 인스턴스를 초기화하도록 사용됨
class HelloBot {
    val greeting: String by lazy { getHello() }

    fun sayHello() = println(greeting)
}

fun getHello() = "안녕하세요"

fun main() {
    val helloBot = HelloBot()
    helloBot.sayHello()
}
  • lateinit
    • 가변 속성에 대한 지연 초기화가 필요할 경우 사용
    • nullable 하게 사용하면 컴파일 에러 발생
    • 항상 non null 타입
    • 값을 초기화하지 않고 사용하게 되면 예외 발생
    • 스프링 등의 프레임워크 또는 라이브러리에서의 DI와 외부에서 초기화 하는 경우에 염두하고 만들어진 기능
    • 초기화 전에 사용하더라도 컴파일 에러가 발생하지 않으므로 주의해야 함
    • 클래스 내부에서는 isInitialized 메서드 이용해서 초기화 여부 확인 가능
class Example {

    lateinit var text: String

    fun printText() {
        println(text)
        text = "Hello"	// UninitializedPropertyAccessException 발생
    }
}


fun main() {
    val example = Example()
    example.printText()
}
class Example {

    lateinit var text: String

    fun printText() {
        if (!this::text.isInitialized) {	// 초기화 여부 확인
            text = "Hello"
        }
        println(text)
    }
}


fun main() {
    val example = Example()
    example.printText()
}