[Kotlin] Value Class

Value Class란 무엇일까

Posted by 동식이 블로그 on August 10, 2022

[Kotlin] Value Class

Value Class

객체를 생성하는 비용을 줄여주는 class

1
2
3
4
5
6
@JvmInline
value class Color private constructor(val rgb: Int) {
  companion object {
    fun rgb(rgb: Int) = Color(rgb)
  }
}

위 처럼 사용이 가능하다

생성시 주의사항

  • Primary constructor is required for value class
  • Inline class must have exactly one primity constructor parameter

그렇다면 언제 사용이 가능할까?

When use Value Class

1
changeBackground(000000)

저 한줄만 보자면 000000이 의미하는게 rgb인지 어떤건지 이해하기 힘들다. 이럴때 래퍼클래스를 사용한다

1
changeBackground(Color.rgb(000000))

이런식으로 Color 클래스와 같은 래퍼클래스를 사용하게 되면 함수의 가독성은 좋아지지만, 함수를 호출할 때마다 객체를 생성해서 호출해야 한다는 비용이 발생하게 된다.

이러한 상황에서 비용을 줄여보자! 라고 나온게 inline class , 즉 value class 이다.

최적화

그렇다면 value class는 어떻게 비용을 줄이는걸까?

1
2
3
4
5
6
@JvmInline
value class Color private constructor(val rgb: Int) {
  companion object {
    fun rgb(rgb: Int) = Color(rgb)
  }
}

value 키워드

  • value 키워드를 통해 value class를 정의할 수 있다
  • 이렇게 정의된 value class는 컴파일러에 의해 최적화의 대상이 된다

@JvmInline Annotation

  • Specifies that given value class is inline class라는 설명이 되어있다

그렇다면 직접 바이트코드를 뜯어보자

1
2
3
fun manglingTest(message: String, rgb: Color) {
    println("message : $message, duration: $duration")
}

아래와 같은 코드로 변환된다

1
2
3
4
5
public final void manglingTest_PX5PXBo/* $FF was: manglingTest-PX5PXBo*/(@NotNull String message, int rgb) {
    Intrinsics.checkNotNullParameter(message, "message");
    String var3 = "message : " + message + ", duration: " + Color.toString-impl(rgb);
    System.out.println(var3);
 }

manglingTest_agtYESI 이렇게 변환이 된다. 이름이 왜 이렇게 변하는걸까? 바로 맹글링처리를 해주기 때문이다.

맹글링?

소스 코드에 선언된 함수 또는 변수의 이름을 컴파일러 단계에서 컴파일러가 일정한 규칙을 가지고 변형하는 것을 의미한다

객체지향적 프로그래밍을 위해 컴파일러가 오버로딩과 네임스페이스별 함수 및 변수를 구별할 수 있도록 함으로써 에러 없이 정상적으로 동작하는 프로그램을 만들기 위해 필요하다

이렇게 맹글링은 하는 이유는 다른 inline 클래스의 인스턴스를 사용하는 함수를 원활하게 오버로드하고, inline 클래스의 내부 제약 조건에 위반되는 Java 코드의 우발적 호출을 방지할 수 있기 때문이다.

이렇게 컴파일러의 맹글링 과정을 거쳐 color 변수는 컴파일 중에 Color 유형을 갖지만 바이트코드에서 Int로 대체된다.

하지만 이 변수를 컬렉션에 저장하거나 제네릭 함수에 전달하면 Color 일반 객체에 박싱이된다.

1
2
3
genericFunc(color)         // boxed
val list = listOf(color)   // boxed
val first = list.first()   // unboxed back to primitive

vs Data Class

  • 자동으로 생성하는 메서드가 다르다

    • data class
      • equals, toString, hasCode, copy, componentN
  • value class - equals, toString, hasCode

  • === 연산 허용 여부

    • data class는 ==, === 모두 허용
    • value class는 ==만 허용
  • Immutable Property

    • data cass는 val, var 둘다 허용
    • value class는 val, 즉 immutable만 허용
  • one property

    • value class에 프로퍼티를 하나만 정의 가능하다.

참고사이트