부가 비용 없이 타입 안전성 추가: 인라인 클래스

유우선·2026년 2월 1일

Kotlin Study📚

목록 보기
8/32

함수에 잘못된 의미의 값을 전달하는 것을 방지하기 위해 클래스의 객체를 파라미터로 전달할 수 있다.

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¢
}

대부분 기본 타입 값의 의미를 명확하게 하고 싶을 때 인라인 클래스를 사용할 것이다.

일반적인 숫자 타입의 값으로 측정한 값의 단위를 표현하거나,

다른 여러 문자열의 서로 다른 의미를 구분하고 싶을 때 인라인 클래스를 사용한다.

인라인 클래스를 사용하면 함수를 호출하는 쪽에서 실수로 잘못된 의미의 값을 전달하는 경우를 막을 수 있다.

0개의 댓글