안드로이드/Kotlin

[Kotlin] lateinit과 by lazy

block_626 2022. 4. 29. 19:05

늦은 초기화

코틀린에서는 변수 선언을 먼저하고 초기화는 뒤로 미루는 기능들을 제공한다.

초기화를 늦추면 좋은 점이, 사용할지 모르는 데이터를 미리 초기화할 필요가 없어서 성능 향상에 도움이 된다.

만약 예를 들어서 어떤 계산 결과값이 필요해서 변수 a를 사용할 예정인데, a의 첫 상태를 정의하기 어려울 때 어떻게 하는것이 좋을까?

var a: String? = null

어차피 이후에 분명 사용할 녀석인데 굳이 위험하게 초기상태로 null로 고정을 해주어야 하나? 라는 것이다.

이럴때를 위해 제공되는 문법이 lateinint과 by lazy이다.

 

lateinit 

lateinit을 사용하면 변수의 값을 지정하는 작업을 뒤로 미룰 수 있다. Nullable 하지 않은 변수를 선언하면서 Assign 하는 작업을 뒤로 미루고 싶을때는 lateinit 키워드를 사용하면 가능하면 된다.

lzateinit은 var에만 사용할 수 있다.

또한, primitive type에 적용할 수 없습니다. primitive type은 Int, Boolean, Double 등의 코틀린에서 제공하는 기본적인 타입을 말한다.

아래처럼 val을 사용하거나, Int를 사용한 코드는 컴파일 에러가 발생한다.

lateinit val a: Boolean // compile error
lateinit var width: Int // compile error

그리고 lateinit 프로퍼티는 custom getter/setter를 설정할 수 없습니다. 또한 non-null 프로퍼티만 사용이 가능하다.

lateinit으로 변수를 선언해 주었다면 사용 전에 꼭 초기화 단계를 거쳐야 한다.

 

정리하면 lateinit의 특징은 다음과 같다.

  • var 프로퍼티만 사용 가능
  • primitive type(Int, Boolean)은 사용할 수 없음
  • Custom getter/setter를 만들 수 없음
  • Non-null 프로퍼티만 사용 가능

by lazy

by lazy 키워드는 lateinit과 비슷하게 값을 지정하는 작업을 미루는 작업인데 assign 되는 시점이 변수를 호출하는 시점이다.

by lazy는 이렇게 사용된다.

val x : String by lazy { "Initialized!" } 
println(x)

처음에 val 형태의 x가 나중에 사용될 것이며, '사용 시에' 어떤 값이 들어가는지를 정해 준다.

이후에 따로 지정할 필요는 없고, 두 번째 줄과 같이 x를 사용하는 첫 순간에 x에 값이 들어간다.

 

이 코드에서는 사실 by lazy가 필요하지는 않다. 초기화될 값이 정해져 있기 때문이다.

하지만 만약 x가 다른 계산식의 영향을 받거나, 다른 변수의 초기화 이후에만 값을 알 수 있다면 이 방식을 사용할 수 있다.

 

이를테면 이런 상황이다.

lateinit var inputValue : String 
val x : Int by lazy { inputValue.length } 
inputValue = "Initialized!"
println(x)

이 코드의 경우, x는 inputValue라는 문자열의 길이를 받아와야 한다.

그런데 inputValue는 lateinit으로 설정되어 있기 때문에, 아직 그 길이를 알 수가 없다.

 

무조건 inputValue가 제대로 값을 가지게 된 후에 x를 활용할 예정이라면, 이 때 이용할 수 있는 문법이 by lazy다.

x는 변수가 '처음 사용되는 순간'인 4번째 줄의 출력문에서 inputValue.length로 초기화된다.

 

x를 사용하기 전에 inputValue 친구만 초기화를 해 주었다면,

어디에서 호출하든 특별히 추가적인 선언문을 작성할 필요가 없는 것이다.

 

lateinit vs lazy

둘은 비슷해보이지만 많은 차이점이 있습니다.

  • lazy는 val 프로퍼티에만 사용할 수 있지만, lateinit은 var에만 사용할 수 있습니다
  • 그렇기 때문에 lateinit은 immutable(불변) 프로퍼티가 아닙니다.
  • lateinit은 nullable 또는 primitive type의 프로퍼티를 사용할 수 없습니다. 반면에 lazy는 모두 가능합니다.
  • lateinit은 직접적으로 프로퍼티를 갖고 있는 구조지만(자바에서 field를 갖고 있음), lazy는 Lazy라는 객체 안에 우리가 선언한 field를 갖고 있습니다. 그래서 lazy의 프로퍼티를 직접 변경할 수 없습니다.

한눈에 비교하자면 기본적으로 둘 다 '늦은 초기화'를 하기 위한 것이다.

본질적인 목적은 비슷하지만 가변서엥 관한 특성이 갈리며, 이에 따라 용법을 구분하여 사용한다.

 

값 변경이 가능한가??

  • lateinit : 가능하다 (var 사용) 
  • by lazy : 불가능하다 (val 사용)

용법 구분??

  • lateinit : 초기화 이후에 계혹하여 값이 바뀔 수 있을 때
  • by lazy : 초기화 이후에 읽기 전용 값으로 사용할 때

'안드로이드 > Kotlin' 카테고리의 다른 글

[Kotlin] Toolbar Custom해보기  (0) 2022.09.28
[Kotlin] Splash 화면 만들기  (0) 2022.08.16
[Kotlin] var와 val의 차이점  (0) 2022.08.06
[Kotlin] 데이터 클래스(Data class)  (0) 2022.07.30
[Kotlin] View Binding  (0) 2022.07.27