본문 바로가기

Kotlin

[Kotlin] lateinit와 by lazy 비교

kotlin에서 lateinit과 by lazy 둘 다 변수를 늦게 초기화 할 때 사용하는 방법이다.

그렇다면 늦게 초기화하는 것은 무엇이고, 둘의 차이점은 무엇일까?

 

코틀린은 변수 선언할 때 객체를 바로 할당하지 않는 경우 컴파일이 되지 않는다.

그래서 변수 선언시 값을 초기화 하지 않거나, nullable 타입으로 만들어주지 않으면 컴파일 에러가 발생한다.

또한 nullable 타입으로 선언해도 코틀린에선 null 초기화 사용을 추천하지 않는다.

 

하지만 개발을 하다보면 변수 선언 시 초기값이나 상태를 정의 하기 어려운 경우가 있다.

즉 변수에 객체를 할당 하는 것을 선언과 동시에 할 수 없을 경우,

변수만 일단 선언하고 나중에 값을 사용할 때 값을 입력해주는 늦은 초기화 / 초기화를 지연, lateinit / lazy 프로퍼티를 활용할 수 있다.

 

먼저 lateinit에 대해 알아보자.

 

lateinit (늦은 초기화)

초기화 이후 값의 변화가 생길 수 있다면, lateinit을 사용하는 것이 좋다.

lateinit var a : String
a = "value"
println(a)

 

먼저 a를 선언하고, a가 나중에 사용될 것이며 Int 타입이라는 것만 정해준다.

a가 사용될 때 값을 지정해준다. (값을 반드시 지정해주어야 한다!)

초기값을 할당하기 전까지 변수를 사용할 수 없다.

이때 a는 Int? 타입으로 선언할 수 없다. 즉 null 값을 넣을 수 없다. 

 

이러한 점에서 lateinit을 사용하면 null 허용 표시(!!)를 하는 번거로움을 피할 수 있다.

null을 허용하지 않으면서 null 허용 표시 없이 사용할 수 있기 때문이다. (!! 역할)

 

또한 lateinit의 경우 Int, Long, Double, Float와 같은 기본형 타입 프로퍼티에는 사용할 수 없다.

 

  • var 프로퍼티 사용
  • null 허용하지 않음
  • Primitive type 사용 불가능
  • get(), set() 사용 불가능
  • 생성자에서 사용 불가능

lateinit 변수의 초기화를 하였는지 여부를 확인할 때는 

변수이름 앞에 ::a.isInitialized 로 확인할 수 있다.

 

fun main() {
    val a = LateInitSample()

    println(a.getLateInitText())
    a.text = "새로 할당한 값"
    println(a.getLateInitText())
}

class LateInitSample {
    lateinit var text : String
    fun getLateInitText(): String {
        if(::text.isInitialized) {
            return text
        }
        else{
            return "기본값"
        }
    }
}

 

 

다음으로 by lazy에 대해 알아보자.

by lazy (초기화 지연)

변수를 사용하는 시점까지 초기화를 자동으로 늦춰주는 lazy delegate properties (지연 대리자 속성)이다.

lateinit 달리 val 변수에 by 키워드를 사용하여  lazy 람다함수에 초기함수를 사용한다.

Val a : Int by lazy {1}
...
println(a) // 이 시점에 1로 초기화 됨

참고로 람다함수로 초기화가 진행되므로 여러개의 구문이 들어갈수 있으며  마지막 구문의 결과가 변수에 할당된다.

 

반드시 값을 초기화 할 필요 없거나 (nullable),

초기화 이후 값의 변화 없이, read-only로 사용된다면 by-lazy를 사용하는 것이 좋다.

 

fun main() {
    val number: Int by lazy {
        println("초기화")
        7
    }

    println("코드 시작")
    println(number)
    println(number)
}

실행 결과는 다음과 같다.

코드 시작
초기화
7
7

코드를 시작한 뒤 number를 처음 출력할 때 lazy함수를 통해 초기화가 진행되었음을 알 수 있다.

두번째 number를 출력할 때는 이미 초기화를 했기 때문에 다시 초기화 구문을 실행하지 않음을 있다.

 

만약 변수가 다른 코드의 영향을 받아 값이 정해진다면 어떻게 활용할 수 있을까?

lateinit var str : String

val strLength : Int by lazy { str.length }
str = "apple"
println(strLength)

 

이 코드를 보면 str의 값이 정해지기 전까지 strLength 변수의 값을 알기 어렵다.

str 값이 정해진 후 strLength값이 초기화되기 위해 by Lazy를 활용할 수 있다.

  • val 프로퍼티 사용
  • nullable
  • Primitive type 사용 가능
  • get(), set() 사용 불가능
  • 생성자에서 사용 불가능

 

lateinit과 by lazy 사용법과 차이에 대해 알아 보았다.

가장 큰 차이점은 초기화 후 값을 변경할 수 있는지 없는 지이다.

늦은 초기화, 초기화 지연은 상황에 따라 변수를 사용하는 방법을 더 세세하게 할 수 있다는 장점이 있다.