함수에 잘못된 의미의 값을 전달하는 것을 방지하기 위해 클래스의 객체를 파라미터로 전달할 수 있다.
class UsbCent(val amount: Int)
fun addExpense(expence: UsbCent) {
/*...*/
}
fun main() {
addExpense(UsbCent(147))
}
하지만 이 방법은 함수를 호출할 때마다 객체를 생성한다.
함수를 아무 많이 호출한다면 가비지 컬렉터에 의해 제거돼야 하는 객체를 수없이 많이 만들게 된다.
따라서 성능 이슈가 발생할 수 있다.
이럴 때 인라인 클래스를 사용하면 성능을 희생하지 않고 타입 안전성을 얻을 수 있다.
인라인 클래스를 만드려면 value 키워드를 사용하고, @JvmInline 어노테이션을 붙여야 한다.
@JvmInline
value class UsdCent(val amount: Int)
이렇게 변경하면 UsbCent 래퍼 타입이 제공하는 타입 안전성을 포기하지 않으면서 불필요한 객체를 생성하는 비용을 줄일 수 있다.
실행 시점에 UsbCent의 인스턴스는 감싸진 프로퍼티로 대체된다.
따라서 이런 클래스를 인라인 클래스라고 부른다.
클래스의 데이터가 사용되는 지점에 인라인된다.
‘인라인’으로 표시하려면 클래스가 프로퍼티를 하나마 가져야 하며, 그 프로퍼티는 주 생성자에서 초기화돼야 한다.
인라인 클래스는 클래스 계층에 참여하지 않는다.
즉, 인라인 클래스는 다른 클래스를 상속할 수 없고 다른 클래스도 인라인 클래스를 상속할 수 없다.
하지만 인라인 클래스는 인터페이스를 상속하거나, 메서드를 상속하거나, 계산된 프로퍼티를 제공할 수 있다.
interface PrettyPrintable {
fun prettyPrint()
}
@JvmInline
value class UsbCent(val amount: Int): PrettyPrintable {
val salesTax get() = amount * 0.06
override fun prettyPrint() = println("${amount}¢")
}
fun main() {
val expense = UsbCent(1_99)
println(expense.salesTax) // 11.94
expense.prettyPrint()// 199¢
}
대부분 기본 타입 값의 의미를 명확하게 하고 싶을 때 인라인 클래스를 사용할 것이다.
일반적인 숫자 타입의 값으로 측정한 값의 단위를 표현하거나,
다른 여러 문자열의 서로 다른 의미를 구분하고 싶을 때 인라인 클래스를 사용한다.
인라인 클래스를 사용하면 함수를 호출하는 쪽에서 실수로 잘못된 의미의 값을 전달하는 경우를 막을 수 있다.