Kotlin은 프로퍼티(property)의 지연 초기화를 위해 lateinit과 lazy라는 두 가지 서로 다른 메커니즘을 제공합니다. 두 기능 모두 값의 초기화 시점을 실제로 필요한 순간까지 미루는 역할을 하지만, 동작 방식과 적용 조건이 다르기 때문에 상황에 맞게 선택하는 것이 중요합니다.
이 글에서는 lateinit과 lazy의 차이점을 살펴보고, 실제 애플리케이션에서 각각을 어떻게 활용할 수 있는지 예제와 함께 알아보겠습니다.
lateinit이란?
변수를 lateinit으로 선언하려면 해당 변수 선언부에 lateinit 키워드를 붙이기만 하면 됩니다. 다만 Kotlin에서 lateinit을 사용하려면 다음 조건들을 반드시 지켜야 합니다.
가변 변수(mutable variable)에만 사용할 수 있습니다. 즉,
var키워드와 함께 사용해야 합니다.널이 아닌(non-nullable) 타입에만 허용됩니다.
Int, Boolean 등 원시 타입(primitive type)에는 사용할 수 없습니다.
커스텀 getter나 setter가 정의되어 있지 않은 프로퍼티에만 적용할 수 있습니다.
lateinit은 의존성 주입(DI) 프레임워크나 Android의 뷰 바인딩처럼, 객체 생성 이후 외부에서 값이 할당되는 경우에 특히 유용합니다.
lateinit 예제
아래 예제에서는 변수를 lateinit으로 선언하고, 프로그램 전체에서 이를 어떻게 접근하는지 확인해 보겠습니다. ::name.isInitialized를 사용하면 값이 할당되었는지 여부를 안전하게 검사할 수 있습니다.
class Tutorial {
lateinit var name: String
fun checkLateInit() {
// 값이 할당되어 있는지 검사
if (this::name.isInitialized) {
println("이미 초기화된 값: $name")
} else {
// name 초기화
name = "www.tutorialspoint.com/"
println(name)
}
}
}
fun main() {
val obj = Tutorial()
obj.checkLateInit()
}실행 결과
위 코드를 실행하면 다음과 같은 출력이 생성됩니다.
www.tutorialspoint.com/
lazy 초기화란?
Kotlin은 효율적인 메모리 관리를 위해 lazy 초기화(lazy initialization) 기능을 제공합니다. lazy 키워드를 사용하면 해당 객체는 실제로 호출되는 시점에 생성되며, 그 전까지는 객체가 만들어지지 않습니다.
lazy()는 람다(lambda)를 인자로 받아 Lazy 인스턴스를 반환하는 함수입니다. 반환된 인스턴스는 위임(delegate)으로 동작하여, 적용된 프로퍼티가 처음 접근될 때 람다를 실행하고 그 결과를 캐싱합니다. 즉, 불필요한 객체 초기화를 방지하기 위해 설계된 기능입니다.
널이 아닌(non-nullable) 변수에 사용하는 것이 일반적입니다.
변수는 반드시 val로 선언해야 하며,
var는 허용되지 않습니다.객체는 단 한 번만 초기화되며, 이후에는 캐시된 값을 그대로 반환받습니다.
애플리케이션에서 실제로 사용되기 전까지는 객체가 초기화되지 않습니다.
lazy 예제
다음 예제에서는 lazy 변수 "myName"을 선언합니다. 초기화 블록은 처음 호출될 때 단 한 번만 실행되며, 값이 초기화된 후에는 애플리케이션 전체에서 동일한 값을 기억합니다. lazy 초기화로 한 번 할당된 값은 다시 재할당할 수 없습니다.
class Demo {
val myName: String by lazy {
println("Welcome to Lazy declaration")
"www.tutorialspoint.com"
}
}
fun main() {
val obj = Demo()
println(obj.myName)
println("같은 객체에 대한 두 번째 호출--" + obj.myName)
}실행 결과
위 코드를 실행하면 다음과 같은 출력이 생성됩니다.
Welcome to Lazy declaration
www.tutorialspoint.com
같은 객체에 대한 두 번째 호출--www.tutorialspoint.com
출력 결과에서 볼 수 있듯이 "Welcome to Lazy declaration" 문구는 첫 번째 호출 시에만 출력됩니다. 두 번째 호출부터는 초기화 로직을 다시 실행하지 않고 캐시된 값이 바로 반환되기 때문입니다.
lateinit vs lazy 비교 정리
| 구분 | lateinit | lazy |
|---|---|---|
| 선언 키워드 | var만 가능 | val만 가능 |
| 원시 타입 | 사용 불가 | 사용 가능 |
| 초기화 시점 | 외부에서 직접 할당 | 첫 접근 시 자동 초기화 |
| 재할당 | 여러 번 가능 | 불가(단 한 번만 초기화) |
| 주요 용도 | DI 프레임워크, Android 뷰 바인딩 등 | 생성 비용이 큰 객체의 지연 생성 |
마무리
정리하면, 값을 나중에 직접 할당해야 하는 경우에는 lateinit이 적합하고, 최초 접근 시 자동으로 한 번만 초기화되기를 원하는 경우에는 lazy가 적합합니다. 두 기능의 특성과 제약 조건을 정확히 이해하고 있으면, 더 안전하고 효율적인 Kotlin 코드를 작성할 수 있습니다.