inline으로 만들수 있는건 함수 뿐만이 아니라, 객체 또한 가능합니다.
프로퍼티가 하나인 클래스 앞에 inline(1.5부터는 value class가 되었습니다.)을 붙이면
해당 객체를 사용하는 위치가 모두 해당 프로퍼티로 교체 됩니다.

1.5부터 바뀐 inline class
class ValueClass {
fun reserveAlarm(alarmMessage: String, duration: Duration) =
println("$duration.millis millis 후에 [$alarmMessage] 알람이 울립니다.")
fun main() {
reserveAlarm("학교 간다", Duration.seconds(2))
}
}
class Duration private constructor(
val millis: Long,
) {
companion object {
fun millis(millis: Long) = Duration(millis)
fun seconds(seconds: Long) = Duration(seconds * 1000)
}
}
위와같이 시간을wrraping class로 만든 코드가 있습니다.
해당 코드를 바이트로 검사해봅시다.
public final class ValueClass {
public final void reserveAlarm(@NotNull String alarmMessage, @NotNull Duration duration) {
Intrinsics.checkNotNullParameter(alarmMessage, "alarmMessage");
Intrinsics.checkNotNullParameter(duration, "duration");
String var3 = duration + ".millis millis 후에 [" + alarmMessage + "] 알람이 울립니다.";
System.out.println(var3);
}
public final void main() {
this.reserveAlarm("학교 간다", Duration.Companion.seconds(2L));
}
}
호출할때마다 Duration 인스턴스가 재생성됩니다.
Effective Kotlin에서도 아래와 같은 예시가 제공되고 있습니다.
val name = Name("최우성")
name.greet()
// 그냥 Class
public final void name() {
Name name = new Name("최우성");
name.greet();
}
//value Class
public final void name() {
//스트링으로 변환된다.
String name = Name.constructor-impl("최우성");
Name.greet-impl(name);
}
inline Class는 다른 자료형을 새로운 자료형으로 래핑해서 만들때 많이 사용되며, 어떠한 오버헤드도 발생하지 않습니다.
inline class는 아래와 같은 상황때 사용됩니다.
측정단위 표현
타입오용으로 발생하는 문제 방지
아까 전 코드로 돌아가 봅시다.
fun reserveAlarm(alarmMessage: String, duration: Int) =
println("$duration.millis millis 후에 [$alarmMessage] 알람이 울립니다.")
fun main() {
reserveAlarm("학교 간다", 2000L)
}
여기서 시간은 어떤 단위일까요? 1000L이 1초일까요??
어떤 단위인지 명확하지 않습니다. 이러한 측정 단위 혼동은 굉장히 큰 문제를 초래할 수 있습니다.
여기서 문제를 해결하는 가장 쉬운 방법은 parameter에 측정단위를 붙여주는 것입니다.
fun reserveAlarm(alarmMessage: String, timeMillis: Int) =
println("$duration.millis millis 후에 [$alarmMessage] 알람이 울립니다.")
fun main() {
reserveAlarm("학교 간다", 2000L)
}
하지만 리턴값이 측정단위라면요..?
fun decideAboutTime() : Int {}
가장 좋은 방법은 인라인 클래스를 활용하여 타입에 제한을 거는것입니다.
fun reserveAlarm(alarmMessage: String, duration: Duration) =
println("${duration.millis} millis 후에 [$alarmMessage] 알람이 울립니다.")
fun main() {
reserveAlarm("학교 간다", Duration.seconds(2))
}
fun decideAboutTime(): Duration {
return Duration.millis(1000L)
}
만약 어떤 데이터 클래스에 property가 매우 혼동된다고 생각해봅시다.
data class Person(
//그냥아이디
val id: Int,
//학생아이디
val studentId: Int,
//주민 아이디
val addressId : Int,
val name: String,
val age: Int
)
전부 Int이기도하고, ID이기 헷갈려서 혼동될 수 있습니다.
이것을 inline Class를 통해 오버헤드 없이 구분할 수 있습니다.
data class Person(
val id: Int,
val studentId: StudentId,
val addressId: AddressId,
val name: String,
val age: Int,
)
@JvmInline
value class StudentId(val id: String)
@JvmInline
value class AddressId(val id: String)

Value Class를 사용하면, 오버 헤드 없이 타입을 래핑할 수 있습니다.
특정 단위들을 사용하거나, 의미가 명확하지 않은 타입을 사용할때 꼭 쓰도록 합시다.
Effective Kotlin Item 49: Consider using inline value classes