질문 정리

코틀린과 지연 초기화, UninitializedPropertyAccessException 방지법

five2week 2023. 3. 15. 21:19

이번에 지연초기화에 대해서 배웠는데 너무 필요했던 내용들이라서 정리해두려고 합니다.

 


lateinit

코틀린에서 지연 초기화란 객체의 초기화를 객체가 필요한 시점에 이루어지도록 늦추는 것을 의미합니다.원래는 선언하는 즉시 초기화 해야하는 것을 초기 값을 입력하지 않고, 이후에 초기화 할 수 있습니다. 따라서 객체가 다른 객체에 의존하는 경우 해당 객체들이 모두 생성되기 전까지 초기화를 지연시킬 수 있습니다.

 

lateinit 지연 초기화를 사용하면서 주의할 점이 있습니다. 변수가 초기화 되기 전에 호출되는 경우, UninitializedPropertyAccessException이 발생할 수 있습니다. 이러한 문제를 방지하기 위해서 호출하기 전에 초기화가 되었는지 체크하는 것이 좋습니다.

class MyClass {
    lateinit var myVariable: MyObject

    fun init() {
        if (::myVariable.isInitialized) {
            // 초기화되었을 경우에만 호출
            myVariable.doSomething()
        }
    }
}

위와 같이 사용하면 lateinit을 사용하면서 UninitializedPropertyAccessException을 방지할 수 있습니다. 하지만 변수의 초기화를 늦게 하는 것은 그만큼 코드의 복잡도를 높입니다. 때문에 가능한 경우, 변수를 즉시 초기화하고 lateinit 키워드를 사용하지 않는 것이 좋습니다.

 

위 코드에서 if (::myVariable.isInitialized)을 검사하는 이유는 "::"는 프로퍼티를 참조하기 위한 멤버 참조 연산자로, 값을 참조하는 것이 아닌 해당 변수나 함수의 메모리 주소값을 보고있기 때문입니다. 따라서 이를 사용하여 해당 변수가 초기화되었는지 여부를 확인하는 등의 작업을 수행할 수 있습니다. 그래서 위의 조건문은 ::myVariable을 통해 변수의 참조를 전달하면서, 변수가 초기화 되었는지 여부를 확인한다는 뜻을 가지고 있습니.  즉, lateinit 변수가 초기화되지 않았을 때 myVariable 자체를 사용하면 예외가 발생하기 때문에 ::myVariable을 사용하여 변수의 참조를 확인하고, isInitialized를 사용하여 초기화되었는지 여부를 검사합니다.

 


by lazy

또다른 방법으로는 by lazy를 사용하는 방법이 있습니다. 이는 객체가 생성될 때 초기화를 수행하지 않고, 해당 객체를 실제로 사용하는 시점에 초기화를 수행합니다. 이는 객체를 생성하는데 드는 비용과 객체를 사용하지 않을 때 불필요한 낭비를 줄일 수 있습니다. 불변 변수인 val에서만 사용 가능합니다. val이므로 값을 다시 변경할 수 없습니다.

 

private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

code block을 수행하는 것이므로, multi thread 동작시 고려가 있어야합니다. 아래와 같은 3가지 모드를 지정할 수 있습니다.

 

private val myObject by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
    MyObject()
}

private val myObject by lazy(LazyThreadSafetyMode.PUBLICATION) {
    MyObject()
}

private val myObject by lazy(LazyThreadSafetyMode.NONE) {
    MyObject()
}

 

SYNCHRONIZED : 락을 사용해 단일 스레드만이 사용하는 것을 보장(기본값)

PUBLICATION : 여러 군데서 호출될 수 있으나 처음 초기화된 후 반환값을 사용, 캐시와 같은 용도로 사용가능

NONE : 락을 사용하지 않기 때문에 빠르지만 다중 스레드가 접근할 수 있음(값의 일관성 보장 불가능)

 

참고 링크

https://kotlinlang.org/docs/properties.html#checking-whether-a-lateinit-var-is-initialized

 

Properties | Kotlin

 

kotlinlang.org

https://kotlinlang.org/docs/delegated-properties.html#lazy-properties

 

Delegated properties | Kotlin

 

kotlinlang.org