[Kotlin] Inline class
참고: 이 글은 Kotlin - Inline classes 공식 문서를 번역한 글입니다.
비즈니스 로직에서 primitive 타입을 감싸야 하는 경우가 가끔 생긴다. 예를 들어 ``Card``의 고유 아이디를 ``val id: Long``으로 나타낼 수도 있지만, ``CardId`` 클래스를 이용하여 표현하면 의미를 더 명확히 드러낼 수 있다.
겨우 아이디 하나 때문에 그렇게까지 해야 하나 싶기도 하지만, 멤버 변수의 값에 제한이 있는 경우 ``inline class``를 유용하게 사용할 수 있다. 예를 들어 ``month``라는 변수는 1부터 12까지의 값만을 가질 수 있으므로 13 이상의 값이 주어지면 값을 할당하지 않도록 코드를 짤 수도 있다.
이런 경우 Kotlin의 ``inline class``를 사용해볼 수 있다. ``inine class``는 값을 저장하는 용도로만 사용해야 한다.
``inline class``
다음과 같이 ``inline class``를 선언할 수 있다.
@JvmInline
value class Password(private val s: String)
자바 바이트코드로 컴파일하는 경우 ``@JvmInline`` 어노테이션을 추가해야 하며, ``class`` 앞에 ``inline``이 아닌 ``value``를 쓴다는 점에 주의하자. ``inline``은 deprecate되었다.
Inline class는 생성자에서 초기화되는 변수 하나만을 가져야 한다. 실제 런타임에서는 객체가 생성되지 않고, 감싸진 변수가 직접 할당된다.
val password = Password("Some password")
예를 들어, 위의 코드는 ``Password`` 객체를 만들지 않는다. 대신 문자열 자체가 할당되고, 런타임에서 ``password`` 변수에는 자바 문자열이 할당된다. 이제 ``inline``이라는 말을 이해할 수 있을까?
멤버 변수? 멤버 함수?
Inline class는 ``init`` 블럭, 멤버 변수, 멤버 함수를 가질 수 있다. ``lateinit``이나 위임된 변수는 선언할 수 없다.
@JvmInline
value class Name(val s: String) {
init {
require(s.length > 0) { }
}
val length: Int
get() = s.length
fun greet() {
println("Hello, $s")
}
}
이때 멤버 변수와 함수는 내부적으로 마치 확장 함수처럼 호출된다.
위의 코드에서처럼 ``init`` 블럭 안에서 값을 검사하고, 잘못된 값이 들어오는 경우를 걸러낼 수 있다. 그 다음엔 exception을 던지던가, 에러를 출력하던가 원하는 대로 하면 된다.
상속
Inline class는 인터페이스를 상속받을 수 있다. 하지만 클래스를 상속받을 수는 없으며, 자기 자신이 부모 클래스가 될 수도 없다.