Kotlin

[Kotlin] lateinit, by lazy

서퍼리노 2023. 1. 27. 13:10
728x90
코틀린에서는 늦은 초기화라는 것을 지원합니다.

 

늦은 초기화란?

원래 변수를 선언할 때 해당 변수의 값을 초기화해주어야 합니다.

하지만 늦은 초기화는 변수를 선언하는 시점이 아닌 나중에 선언해 주는 것입니다.

 

늦은 초기화를 사용하는 이유는?

만약에 분명 사용할 것이고, 언제 사용할 건지도 알고 있습니다.

하지만 처음 실행되는 시기에는 값을 정의 내릴 수 없다면 어떡해야 할까요??

 

var a: String? = null

코틀린에서는 이와 같이 널을 이용하여 코드를 선언해 줄 수 있습니다.

하지만 이는 좋은 방법이 아닙니다.

 

코틀린에서는 null을 허용할 수 있는 기능은 지원하긴 하지만

null을 사용하는 것을 지향하지는 않습니다.

 

저 변수는 nullability로 지정이 되어있기 때문에

 

null이 들어갈 수 있는 변수이기 때문에 

변수를 항상 사용할 때마다

항상 null은 아닌지 확인해야 하는 불편함이 있습니다.

 

따라서 null이 들어갈 수는 없지만 나중에 초기화를 시켜주고 싶은 경우가 있기에

늦은 초기화라는 것을 사용합니다.

 

즉, 이런 상황에서 사용하는 것이 lateinit과 lazy입니다.

 

lateinit

사용법

fun main () {
    lateinit var text: String

    val result1 = 50

    text = "Result: $result1"
    println(text)

    val result2 = 120

    text = "Result: ${result1 + result2}"
    println(text)
}

해당 코드를 살펴본다면 

처음에 lateinit이라는 키워드를 var과 함께 사용하여 값을 지정하지는 않았지만,

선언을 해주었고,

 

그 이후에 대입 연산자를 통하여 값들을 지정해 주었습니다.

 

lateinit을 사용한 것을 보면 한 번 값을 넣어준 이후에

또 한 번 값이 변하는 것을 알 수 있습니다.

 

이를 통해서

lateinit은 늦은 초기화를 해준 이후에 다시 값을 변경할 수 있다는 것을 알 수 있습니다.

 

이를 실행시키면 이런 식으로 정상적으로 코드가 작성됩니다.

 

그렇다면 lateinit을 사용하고, 초기화를 하지 않는다면??

Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property text has not been initialized

lateinit은 사용할 때 아직 초기화가 되지 않았더라면 예외를 발생시켜 줍니다.

 

lateinit이라는 키워드를 사용하기로 컴파일러에게 미리 알려주기 때문에 

런타임에러가 발생하는 것이 아닌 컴파일이 되는 시점에서 에러가 발생하기 때문에 

잠재적인 오류를 방지해 줄 수 있습니다.

 

by lazy

fun main() {
    val text: String by lazy {"initial"}
    println(text)
}

코드를 살펴보자면 이후에 사용될 해당 변수의 타입을 지정해 주고,

해당 코드는 나중에 사용될 것이며, 

사용되는 순간에 해당 값이 초기화됩니다.

 

사실 저 코드에서는 나중에 사용될 값이 

미리 정의되어 있기 때문에 굳이 늦은 초기화를 해줄 필요가 없습니다.

 

fun main() {
    lateinit var text : String
    val textLength : Int by lazy {
        text.length
    }

    text = "Hello_Kotlin")
    println(textLength)
}

하지만 이런 상황 이러면 어떨까요??

 

처음에 선언될 당시에는 사용될 값인

text의 길이에 대해 알지 못하지만,

프로그램이 진행되며 값을 알게 되는 것이다. 

 

따라서 textLength를 사용하기 전에만 지정해 주면 된다는 것이다.

 

위의 코드를 살펴보면 알 수 있듯이

lateinit과 달리 대입을 다시 해주는 것이 아니라

값이 나중에 들어간다는 느낌으로 확인할 수 있습니다.

 

lazy는 val 키워드와 함께 사용되기 때문에

값을 다시 바꿀 수는 없습니다.

 

단 한 번의 늦은 초기화 이후 값을 변경할 수 없다는 것을 알 수 있습니다.

 

Tip
안드로이드에서 이전 액티비티에서 넘어온 Intent Bundle Extra 등을
현재 액티비티 멤버 변수에 by lazy로 받아와, 선언해 두고 사용 시에
intent.extra 등으로 번들을 사용하면, 생명주기를 위반하지 않고
안전하게 클래스 전역에서 사용할 수 있는 값을 갖고올 수 있습니다.

 

비교하기

기본적으로 둘 다 본질적인 목적으로 늦은 초기화를 하기 위함은 비슷합니다.

하지만 가변성에 관한 특성이 갈리며, 이에 따라 용법을 구분하여 사용합니다.

 

비교 lateinit lazy
가변성 가능(var 사용) 불가능(val 사용)
null 허용 허용하지 않음 허용
용법 초기화 이후에 계속 하여 값이 바뀔 수 있을 때 초기화 이후에 읽기 전용으로 값을 사용할 때

 

728x90