함수 뿐만 아니라 하나의 값만 가지고 있는 객체는 inline으로 나타낼 수 있다.
inline class Name(proivate val value: String) {
// ...
}
이러한 클래스는 가능한 경우에 언제든 변경될 수 있다.
val name: Name = Name("Marcin")
// During compilation replaced with code similar to:
val name: String = "Marcin"
inline class Name(private val value: String) {
// ...
fun greet() {
print("Hello, I am $value")
}
}
// Code
val name: Name = Name("Marcin")
name.greet()
// During compilation replaced with code similar to:
val name: String = "Marcin"
Name.`greet-impl`(name)
그래서 우리는 인라인 클래스는 특정 타입을 성능 저하 없이 wrapping 할 때 사용할 수 있다. inline class 주로 사용하는 때는 두 가지가 있다.
- 측정의 단위를 나타낼 때
- 타입을 잘못 사용되는 경우를 방지할 때
interface Timer {
fun callAfter(time: Int, callback: () -> Unit)
}
여기서 time의 단위는 무엇일까? milliseconds, seconds 등등 다양할 수 있다. 따라서 잘못 사용될 가능성이 있다.
interface Timer {
fun callAfter(timeMillis: Int, callback: () -> Unit)
}
그래서 이렇게 property name을 명시적으로 수정하면 전보다는 낫지만 함수를 사용할 때 이름이 노출되지 않는 경우도 있다.
그리고 조금만 호출이 복잡해져도 쉽게 무시될 가능성이 있다.
interface User {
fun decideAboutTime(): Int
fun wakeUp()
}
interface Timer {
fun callAfter(timeMillis: Int, callback: () -> Unit)
}
fun setUpUserWakeUpUser(user: User, timer: Timer) {
val time: Int = user.decideAboutTime()
timer.callAfter(time) {
user.wakeUp()
}
}
그래서 inline class를 사용하면
inline class Minutes(val minutes: Int) {
fun toMillis(): Millis = Millis(minutes * 60 * 1000)
}
inline class Millis(val milliseconds: Int) {
}
interface User {
fun decideAboutTime(): Minutes
fun wakeUp()
}
interface Timer {
fun callAfter(timeMillis: Millis, callback: () -> Unit)
}
fun setUpUserWakeUpUser(user: User, timer: Timer) {
val time: Int = user.decideAboutTime()
timer.callAfter(time) { // ERROR: Type mismatch
user.wakeUp()
}
}
fun setUpUserWakeUpUser(user: User, timer: Timer) {
val time: Int = user.decideAboutTime()
timer.callAfter(time.toMillis()) {
user.wakeUp()
}
}
이는 단위를 계산할 때 유용하게 사용이 되고, 이것을 통해 DSL스러운 extention을 사용할 수 있다.
inline val Int.min
get() = Minutes(this)
inline val Int.ms
get() = Millis(this)
val timeMin: Minutes = 10.min
잘못 사용하는 경우를 줄여주는 예시를 보자.
@Entity(tableName="grades")
classGrades(
@ColumnInfo(name = "studentId")
val studentId: Int,
@ColumnInfo(name = "teacherId")
val teacherId: Int,
@ColumnInfo(name = "schoolId")
val schoolId: Int,
// ...
)
각 Id 값이 전부 Int형이기 때문에 자칫 잘못해서 섞어쓸 가능성이 많다.
inline class StudentId(val studentId: Int)
inline class TeachetId(val teacherId: Int)
inline class SchoolId(val schoolId: Int)
@Entity(tableName="grades")
classGrades(
@ColumnInfo(name = "studentId")
val studentId: Int,
@ColumnInfo(name = "teacherId")
val teacherId: Int,
@ColumnInfo(name = "schoolId")
val schoolId: Int,
// ...
)
'개발 > Kotlin' 카테고리의 다른 글
Item 42: Respect the contract of equals (0) | 2021.10.21 |
---|---|
Item 35: Consider defining a DSL for complex object creation (0) | 2021.09.23 |
Item 28: Specify API stability (0) | 2021.09.16 |
Item 21: Use property delegation to extract common property patterns (0) | 2021.09.02 |