본문 바로가기

코틀린

[Kotlin] 기본 타입과 내부 처리

 코틀린의 모든 변수는 멤버 함수를 가진다는 점에서 일종의 객체로 간주할 수 있다. 예를 들어 Intellij 환경에서 13을 입력한 후 ctrl + space 키를 입력하여 자동완성을 수행하면 아래와 같이 Int형 13에 대한 멤버 함수들을 호출할 수 있다.

intellij 환경에서 13의 멤버 변수를 자동완성으로 불러온 모습

  위와 같은 특징은 JVM을 기반으로 하는 다른 언어인 Java 와도 사뭇 다르다. Java의 경우 int, float 등의 primitive type은 객체가 아니라서 멤버 함수를 가지지 않으며,  raw value로 취급된다. 

 코틀린 언어 수준에서 int, float 등의 Number type 이 마치 객체인 것 처럼 사용되기는 하지만, 실제로 모든 값이 객체 형태로 처리되는 것은 아니다. 이를 알아보기 위해 코틀린 코드를 Java 코드로 디컴파일해보자.

 Intellij에서 좌측의 코틀린 코드를 Java 코드로 디컴파일하면 우측의 결과가 나온다. 즉, 코틀린 수준에서 객체로 취급되었던 Int 형 변수들이 실제로는 단순한 raw value로 취급된다는 것을 알 수 있다. 

 단, 코틀린의 변수가 nullable이라면 말이 달라진다. Java는 primitive type 수준에서 nullable을 표현할 수 없다. 만약 primitive type을 nullable로 표현하고 싶은 경우 Wrapper class를 이용해야 한다.

primitive type wrapper class
byte Byte
boolean Boolean
char Character
double Double
float Float
int Integer
long Long
short Short

  코틀린은 C#, typescript 등 다양한 언어들처럼 ? 을 붙여 primitive type 수준에서 nullable을 표현할 수 있다. 이때 nullable 변수가 실제로 null과 관련된 동작을 수행하면, 컴파일러 수준에서 해당 변수를 wrapper class을 이용하여 처리한다. 이를 알아보기 위해 아래와 같이 코틀린 코드를 작성하고 디컴파일하여 자바 코드와 비교해보자.

코틀린 코드(좌).  해당 코드를 자바로 디컴파일한 코드(중).  출력된 결과(우)

 좌측의 코틀린 코드는 디컴파일을 통해 가운데 자바 코드로 변환되었다. 이때 boxed_ , anotherBoxed_ 및 nullJ 변수들 모두 JVM 수준에서 Integer wrapper class로 처리되는 것을 알 수 있다. 

 여기서 특이한 점은 println(boxedI === anotherBoxedI) 의 결과가 true 로 나온 것이다. boxedI 및 anotherBoxedI는 raw value가 아닌 wrapper class 형태로 처리되므로 둘은 엄연히 다른 객체라고 생각하는 것이 일반적이다. 따라서 객체 단위의 비교를 수행하는 === 연산자의 결과로 false를 기대한다.

 그러나 JVM 수준에서  -128 ~ 127 사이의  Integer 변수를 미리 만들어 두기 때문에 실제로는 같은 객체를 가리킨다. 파이썬에서 작은 숫자들을 미리 저장해두고 해당 값을 사용하는 것처럼 최적화를 위한 선택이다.

 최적화의 또 다른 결과로, nullable 값이라도 실제로 null 과 관련된 동작을 수행하지 않으면 wrapper class로 처리되지 않는다. 위 코드에서 === 비교 부분을 주석처리한 후 디컴파일 해보자.

  앞서 보인 상황과는 달리 현재 코드 상에서 boxed_ 및 anotherBoxed 변수들은 객체 혹은 null 과 관련된 동작을 수행하지 않아 최적화 과정에서 Integer wrapper 클래스로 처리되지 않는다. 특히 현재 상황에서는 값의 비교만 수행하기 때문에 변수를 만드는 것 자체가 의미가 없으므로 변수를 생략하는 최적화를 수행하고 있다.

 null과 관련될 수 있는 경우 최적화 과정을 통해 primitive type 혹은 wrapper class 의 형태를 취한다.

 동일한  Int? 형이지만 boxed_ 및 anotherBoxed 변수들과는 달리 nullable 변수의 "출력" 동작을 수행하고 있으므로 변수 m은 Integer로 처리되었다. 반면 k는 Int? 이지만 내부적인 최적화 과정을 통해 단순한 int 형이 되었다.

 참고로 boxed_ 및 anotherBoxed_ 변수들을 println 을 통해 출력하는 경우, nullable 출력 관련 동작과 관련되어 있기 때문에 Integer wrapper class로 처리된다. 따라서 성능을 위해 nullable 출력은 삼가는 것이 좋을 것 같다.


결론

 코틀린의 기본 타입은 객체처럼 멤버 함수를 가진다. 해당 타입들 중 Int, Float 등 타 언어에서 primitive value로 분류되는 값들은 nullable 적용 여부와 실제 사용 방식에 따라 JVM 상에서 primitive value 혹은 Wrapper class로 처리될 수 있다. 

'코틀린' 카테고리의 다른 글

[코틀린] 자바&코틀린 라이브러리 검색  (0) 2022.11.05
[Kotlin] 상속  (0) 2022.10.10
[Kotlin] 생성자  (0) 2022.09.06
[Kotlin] control flow  (0) 2022.09.06
[Kotlin] 기본 타입  (0) 2022.09.03