늦은 초기화를 위해 lateinit 과 by lazy를 비교한 포스트
클래스 안에서 변수 선언만 해 놓고,
나중에 값을 사용할 때 값을 초기화 시켜 주고 싶을 때가 있다.
이럴 때 사용 가능한 방법 중에는 아래와 같이 일단 null로 설정해 준 뒤 나중에 바꾸는 방법이 있기는 하다.
var x : String? = null
하지만 두 가지, 마음에 걸리는 상황이 생길 수 있다.
1) x의 값이 변할 수는 있지만, 절대 null이어서는 안 되는 경우 -> 처음에 nullable한 <String?>으로 초기화해주는 것이 부적절함
2) x의 값이 처음 한 번만 정해지면 다시는 변하지 않는 경우 -> x가 var일 이유가 없음
애초에 '나중에 쓸 것'이라는 걸 알고 있는데, '왜 처음에 null로 고정을 해 주어야 하냐?' 라는 문제다.
이 문제를 해결 하기 위해 lateinit과 by lazy를 사용한다.
lateinit 조건
lateinit은 꼭 변수를 부르기 전에 명시적으로 초기화 시켜야 한다.
var(mutable)에서만 사용이 가능하다
(var이기 때문에 언제든 초기화를 변경할 수 있다.)
null을 통한 초기화를 할 수 없다.
초기화를 하기 전에는 변수에 접근할 수 없다.
(lateinit property subject has not been initialized 오류 발생)
변수에 대한 setter/getter properties 정의가 불가능하다.
primitive type에서는 활용이 불가능하다.
lateinit var x : String
x = "Initialize here"
println(x)
x에 값이 들어가기 전에 x는 null이 아니라 uninitialized(초기화되지 않음) 상태로 존재하기 때문에 lateinit property subject has not been initialized
오류 발생
by lazy는 late inint에 비해상대적으로 편하게 사용이 가능하며, 실수할 일도 줄어든다.
호출 시점에 by lazy 정의에 의해서 자동으로 초기화를 진행한다.
val(immutable)에서만 사용이 가능하다.
(val이므로 값을 변경하는게 불가능하다.)
초기화를 위해서는 함수명이라도 한번 적어줘야 한다.
lazy을 사용하는 경우 기본 Synchronized로 동작한다.
lateinit var inputValue : String
val x : Int by lazy { inputValue.length }
inputValue = "Initialize here"
println(x)
이 코드의 경우, x는 inputValue라는 문자열의 길이를 받아와야 한다.
그런데 inputValue는 lateinit으로 설정되어 있기 때문에, 아직 그 길이를 알 수가 없다.
무조건 inputValue가 제대로 값을 가지게 된 후에 x를 활용할 예정이라면, 이 때 이용할 수 있는 문법이 by lazy다.
x는 변수가 '처음 사용되는 순간'인 4번째 줄의 println에서 inputValue.length로 자동으로 초기화된다.
x를 사용하기 전에 inputValue 변수만 명시적으로 초기화를 해 주었다면,
어디에서 호출하든 특별히 추가적인 선언문을 작성할 필요가 없는 것이다.