TIL

Inline value classes

https://kotlinlang.org/docs/inline-classes.html

@JvmInline
value class Password(private val s: String)

Members

@JvmInline
value class Person(private val fullName: String) {
    init {
        require(fullName.isNotEmpty())
    }

    constructor(firstName: String, lastName: String) : this("$firstName $lastName") {
        require(lastName.isNotBlank())
    }

    // 인라인 클래스의 프로퍼티 getter 및 함수는 static 함수로 호출된다.
    
    val length: Int
        get() = fullName.length

    fun greet() {
        println("Hello, $fullName")
    }
}

Inheritance

@JvmInline
value class Name(val s: String) : Printable {
    override fun prettyPrint(): String = "Let's $s!"
}

Representation

interface I

@JvmInline
value class Foo(val i: Int) : I

fun asInline(f: Foo) {}
fun <T> asGeneric(x: T) {}
fun asInterface(i: I) {}
fun asNullable(i: Foo?) {}

fun <T> id(x: T): T = x

fun main() {
    val f = Foo(42)

    asInline(f)    // unboxed: Foo 자체를 사용
    asGeneric(f)   // boxed: T 타입의 제네릭으로 사용
    asInterface(f) // boxed: 인터페이스 I 타입으로 사용
    asNullable(f)  // boxed: nullable 타입으로 사용, Foo와 차이는?

    // 아래 f는 id 함수의 매개변수에 패싱될 땐 래퍼 타입 이지만
    // 함수 내부에서 리턴될 땐 원시 타입으로 unboxing된다.
    val c = id(f)
}
@JvmInline
value class UserId<T>(val value: T)

fun compute(s: UserId<String>) // fun compute-<hashcode>(s: Any?)

Mangling

@JvmInline
value class UInt(val x: Int)

// JVM에서 'public final void compute(int x)'로 표현된다
fun compute(x: Int) { }

// 아래도 마찬가지로 JVM에서 'public final void compute(int x)'로 표현된다.
fun compute(x: UInt) { }

Calling from Java code

@JvmInline
value class UInt(val x: Int)

fun compute(x: Int) { }

@JvmName("computeUInt") // 맹글링 비활성화
fun compute(x: UInt) { }

Inline classes vs type aliases

Inline classes and delegation

interface MyInterface {
    fun bar()
    fun foo() = "foo"
}

@JvmInline
value class MyInterfaceWrapper(
  val myInterface: MyInterface
) : MyInterface by myInterface

fun main() {
    val myInterfaceImpl = Object : MyInterface {
      override fun bar() { }
    }
    
    val my = MyInterfaceWrapper(myInterfaceImpl)
    
    println(my.foo()) // prints "foo"
}