Kotlin의 inline class (Kotlin 1.5부터는 "value class"라는 용어),는 최소한의 오버헤드로 타입의 래퍼를 생성할 수 있게 해줍니다. 컴파일 시 이러한 클래스는 내부 값(value)으로 대체되어 객체 생성에 대한 오버헤드를 피합니다.
제한사항 및 특징:
===가 일반적으로 작동하지 않음).예제:
@JvmInline value class UserId(val value: String) fun getUser(id: UserId) { println("Loading user with id: ${id.value}") } val id = UserId("XYZ") getUser(id) // 내부적으로는 단순히 String으로 작업합니다!
사용할 때:
value class를 상속하거나 인터페이스/추상 클래스 계층에서 사용할 수 있나요?
답변: 아니요, value class는 다른 클래스(인터페이스 제외)를 상속할 수 없으며, 상속을 위해 열 수 없고, init 블록 및 기타 비정적 필드를 허용하지 않습니다. 유일한 가능한 방법은 인터페이스를 구현하는 것입니다.
예제:
interface Validatable { fun isValid(): Boolean } @JvmInline value class Email(val raw: String) : Validatable { override fun isValid() = raw.contains("@") }
이야기
Android 애플리케이션은 Parcelable의 매개변수에 value class를 추가한 후 스타트 시간이 급격히 증가했습니다: 잘못된 @Parcelize와 value class가 직렬화의 모든 단계에서 boxing/unboxing을 초래하여 inline의 장점을 무너뜨렸습니다.
이야기
마이크로서비스는 타입 안전성을 위해 UserId와 ProductId에 대해 value class를 적극적으로 사용하기 시작했지만, 많은 곳에서 제네릭 함수가 반사(reflection)를 요구하여 "래퍼"와 함께 작동하지 않았습니다. 유닛 테스트가 의외로 실패하기 시작했고, ClassCastException이 발생했습니다.
이야기
Java에서 마이그레이션된 코드는 최적화를 위해 내부 도메인 클래스를 value class로 대체하기 시작했지만, nullable 필드로 사용함에 따라 예상치 못한 null pointer exception이 발생했습니다. value class는 외부 값이 null일 때만 null이 될 수 있기 때문에, 이는 기존의 불변성을 깨뜨렸습니다.