Int, Double 과 같은 Primitive Type 변수 앞에 lateinit 키워드를 선언하면 다음과 같이 에러가 발생합니다.
먼저 이미 아시겠지만 lateinit은 가변 변수에 한해 지연 초기화를 하고자 할 때 사용하는 키워드입니다. 처음 lateinit 을 접했을 때 String 및 기타 참조 자료형의 경우에는 문제가 없지만 Int와 같은 기본 자료형의 경우 사용할 수 없는 것에 의문이 들었던 기억이 납니다.
lateinit 키워드를 사용한 코틀린 코드를 자바로 디컴파일 해보겠습니다.
IntelliJ 에서 디컴파일 하는 법 (Mac 기준)
Tools -> Kotlin -> Show Kotlin Byte Code -> Decompile
public final class Main {
public String name;
@NotNull
public final String getName() {
String var1 = this.name;
if (var1 != null) {
return var1;
} else {
Intrinsics.throwUninitializedPropertyAccessException("name");
return null;
}
}
public final void setName(@NotNull String var1) {
Intrinsics.checkNotNullParameter(var1, "<set-?>");
this.name = var1;
}
}
name에 대한 getter가 생성되고, getter 내부에서 var1이라는 name과 동일한 String 타입의 지역변수를 생성하여 그것을 name 값으로 초기화시켜주고 있습니다. 그리고 if문을 통해 var1 이 null인지 아닌지 검사하고 있고, null일 경우 Exception을 발생시킵니다.
그러니까 lateinit으로 선언된 변수에 접근할 때, 해당 변수가 아직 초기화되지 않았다면(null이라면) Exception이 발생하게 됩니다. 하지만 name이 만약 Int 라면 어떨까요? Int는 기본 자료형, Primitive type이기 때문에 null 값이 들어갈 수 없습니다. 따라서 if문을 통해 null인지 아닌지를 비교하여 해당 변수의 초기화 여부를 알 수가 없습니다.
그렇다면 왜 Primitive Type은 null 값을 가질 수 없을까요? 그리고 Primitive Type과 Reference Type은 뭐가 다를까요? C언어를 공부할 때 배웠던 call by value 와 call by reference를 떠올리면 쉽게 이해가 될 것 같습니다.
값을 저장하는 가장 작은 단위로, 메모리에 변수가 가지는 값을 저장합니다. 따라서 "값이 없음"을 나타내는 null이 할당될 수 없습니다.
값이 저장된 메모리 주소를 저장합니다. 즉, 값이 저장된 주소값을 저장하기 때문에 null을 할당하면 어떠한 객체도 가리키지 않는 상태로 설정할 수 있습니다.
그런데 저와 비슷한 의문을 가졌던 분들이 계실 것 같습니다. Int가 null 값을 가질 수 없다면, 이 코드는 왜 허용되는 걸까요?
var test2: Int? = null
이 코드도 자바로 디컴파일 해보겠습니다.
public final class Main {
private int test1;
@Nullable
private Integer test2;
}
nullable이었던 test2는 int가 아닌 Integer 타입의 변수로 디컴파일 되었습니다. Integer 클래스는 Wrapper 클래스로, 기본 자료형을 객체로서 다루기 위해 클래스로 만든 것입니다. 따라서 자료형이 아닌 클래스로, null 값 처리가 가능합니다.
결론적으로 primitive type 변수는 null 값이 할당될 수 없기 때문에 lateinit 키워드를 사용할 수 없습니다.
lateinit var vs val lazy
lateinit var name: String // 변수 타입만 지정
name = "Hazel" // 원하는 시점에 초기화 (사용하기 전 반드시 초기화 해주어야 함)
val name by lazy { "Hazel" } // 사용될 시점에 초기화하고자 하는 값을 변수 선언부에서 미리 지정
println(name) // 사용되는 시점에 "Hazel"로 초기화
Kotlin lateinit var에 관하여
Kotlin/Java의 Primitive type, Reference type
[Java] int와 Integer의 차이
[내 맘대로 정리한 Kotlin] lateinit과 by lazy의 차이점