본문 바로가기

안드로이드/Kotlin

6. Effective Kotlin - 함수를 파라미터로 갖는 함수에 inline을 사용하라

함수 타입 파라리터를 갖는 함수에 inline 한정자를 붙여라

inline의 역할은 컴파일 시점에 '함수를 호출하는 부분'을 '함수의 본문'으로 대체하는 역할을 한다.

inline fun repeat(times: Int, action: (Int) -> Unit) {
  for (index in 0 until times) {
    action(index)
  }
}
repeat(10) {
  print (it)
}

위의 repeat는 컴파일 시점에 아래와 같이 교체된다.

for (index in 0 until times) {
  print(index)
}

inline을 사용하면 다음과 같은 장점이 있다.

  • reified를 사용할 수 있다.
  • 더욱 빠르게 동작한다.
  • 비지역(non-local) 리턴을 사용할 수 있다.

reified를 사용할 수 있다.

구버전의 Java는 Generic이 없었다. 그래서 JVM 바이트 코드에는 아직도 Generic이 없다. 즉, 컴파일 시 Generic에 대한 정보가 지워진다. 예를들어 List<Int>가 컴파일되면 List이다.

any is List<*> 오류

fun <T>.printTypeName() {
  print(T::class.simpleName) //오류
}

함수를 inline으로 사용하면 이러한 제한을 무시할 수 있다.

inline fun <reified T>.printTypeName() {
  print(T::class.simpleName)
}

printTypeName<Int>() //Int

컴파일하는 동안 위 코드는 아래로 대체된다.

print(Int::class.simpleName)

더욱 빠르게 동작한다.

함수 호출과 리턴을 위해 jump를 수행하는 과정과 back stack을 추적하는 과정이 없기 때문이다. 함수를 파라미터로 갖지 않는 경우에는 큰 차이가 없다. 앞서 서술한 과정이 없기 때문이다.

그리고, 함수는 일급 객체이다. 함수는 컴파일 시 클래스로 컴파일 된다.

val lambda: () -> Unit = {
  ...
}
class Test$lambda implements Function0<Unit> {
  Unit invoke() {
    ...
  }
}

Function0<Unit> lambda = new Function0<Unit>() {
  ...
}

앞선 포스팅에서 '불필요한 객체 생성을 피하라'의 원칙에 따르면 효율성 측면에서도 좋지 않다.

비지역적(non-local) 리턴을 사용할 수 있다.

fun repeatNoInline(times: Int, action: (Int) -> Unit) {
  for (index in 0 until times) {
    action(index)
  }
}

위의 함수는 내부에서 return을 사용할 수 없다.

repeatNoInline(10) {
  print(it)
  return // 오류
}

이는 함수가 컴파일될 때 객체로 래핑되어서 발생하기 때문이다.

inline fun repeatNoInline(times: Int, action: (Int) -> Unit) {
  for (index in 0 until times) {
    action(index)
  }
}
repeat(10) {
  print(it)
}

반면에 inline을 사용하면 위처럼 사용할 수 있다.

inline의 문제점

  • 가시성 제한이 있을 경우 사용할 수 없다.
  • 재귀적으로 동작할 수 없다.

inline이 inline을 호출하는 경우를 유의해야 한다.

crossinline과 noinline

함수는 인라인으로 만들고 파라미터로 받는 함수는 인라인으로 받고 싶지 않은 경우가 있다. 이럴 때 crossinline/noinline을 사용할 수 있다.

  • crossinline: 파라미터로 인라인 함수를 받지만, 비지역적 리턴을 하는 함수는 받을 수 없다.
  • noinline: 인라인 함수를 받을 수 없다.

crossinline/noinline은 IntelliJ가 적절히 추천해주므로 추상적으로 알고 있어도 된다.