본문 바로가기
📚 Kotlin

[Effective Kotlin] 아이템 47. 인라인 클래스의 사용을 고려하라

by GroovyArea 2023. 2. 3.

이펙티브 코틀린을 거의 다 읽어 간다.

이펙티브 자바나 코틀린이나 백엔드에서 API를 설계하는 것 뿐만 아니라 특정 분야의 사용에 국한되지 않고, 

언어 자체를 어떤식으로 작성해야 하는지를 알려주는 책이다.

 

나는 코틀린으로 라이브러리를 만들고 있지 않기 때문에, 

백엔드 개발에 필요한 부분을 따로 되새기고자 특정 유용한 아이템들만 블로그에 정리한다.

따로 글로 정리하지 않은 나머지 챕터는 읽어가며 코틀린에 대해 더 깊은 이해를 하려 노력한다.

 

Inline 한정자가 참 많이 나왔는데, 거의 고차 함수나 탑 레벨 함수에 많이 사용하는 것으로 학습했다.

성능적 부분에서 객체를 따로 생성하지 않고 함수 내부로 편입되기에 우수하므로, 인상 깊은 개념이었다.

 

톱레벨 함수나 고차 함수를 유틸리티 함수를 제외하면 Inline 한정자를 잘 사용하지 않을 것이기에, 읽기만 하려다가 클래스에서의 사용은

유용하겠다는 생각을 하며 정리해본다.

 

Inline 클래스

inline class Id(private val value: String) {
	// ....
}

// 코드
val id: Id = Id("hello")

// 컴파일 된 코드
val id: String = "hello"

생성자 프로퍼티가 하나인 클래스에 인라인 한정자를 붙이면,

해당 객체를 사용하는 위치가 모두 프로퍼티로 교체된다!!

 

코드로서는 타입을 이용하기에 가독성과 타입 안정성을 보장 받고,

성능적으로는 객체를 생성하지 않기에, 일석이조의 효과를 얻을 수 있다.

 

마치 VO 객체를 보는 것 같다. 성능을 곁들인.

 

쓋!!

 

또 하나, 인라인 클래스의 메서드는 모두 정적 메서드로 만들어진다.

 

인라인 클래스 사용 상황

측정 단위를 표현할 때,

타입 오용으로 발생하는 문제를 막을 때,

 

interface Timer {
	fun callBefore(time: Int, callback: () -> Unit)
}

time의 단위가 명확하지 않다.

이러한 API는 문제점이 분명하다.

 

파라미터에 time. 대신 timeMillis, timeSeconds 라는 네이밍으로 대체할 수 있다.

하지만, 외부에서는 그냥 Int 값이므로 이 역시 명확하지 않다.

 

해서 타입에 제한을 걸어볼 수 있을 것이다.

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 setUpWakeUpUser(user: User, timer: Timer) {
	val time = user.decideAboutTime()
    
    timer.callAfter(time.toMillis()) {
    	user.wakeUp()
    }
}

인라인 클래스로 매핑하여 

명확하게 시간에 대한 타입을 지정할 수 있게 되었다.

물론 내부적으로 int 값을 사용하겠지만!

 

인라인 클래스와 인터페이스

인라인 클래스도 인터페이스를 구현할 수 있다.

 

하지만, 이러한 구성은 인라인으로 동작하지 않는다.

타입의 이점을 활용해 추상화를 진행했지만, 결과적으로 인라인의 장점을 활용하지 못하기 때문에 사용하는 것은 무의미하다.

 

정리

인라인 클래스를 사용하여 성능적 오버헤드 없이 타입을 래핑할 수 있게 되었다.

타입을 적극 활용하여 코드의 안정성을 높이며,

명확한 타입만을 강제할 수 있기에, 앞으로 적극 이용할 것 같다!

반응형