본문 바로가기

코틀린

코틀린: 인라인(inline) 함수와 noinline

인라인(inline) 함수에 관한 보충 설명입니다. 자세한 내용은 쉽게 다가가는 최신 프로그래밍: 코틀린 - 3.6.3 인라인 함수를참고하기 바랍니다. 인라인 함수 개념은 간단합니다. 인라인 함수를 호출하는 문장 대신 이 함수의 전체 코드로 바꿔치기 하는 것입니다. 교재 p.153에 소개한 예제입니다. 

inline fun squared(a: Int, f: (Int) -> (Int)): Int {
    return f(a)
}

fun main() {
    val x = 4
    val result = squared(x) { k -> k * k }
    println("$x * $x = $result")
}

IDEA에서 Tools > Kotlin > Show Kotlin Bytecode 를 선택하면 나타나는 Kotlin Bytecode 창에서 Decompile을 클릭하면 역 컴파일된 자바 코드(보기 쉽도록 일부 코드는 삭제)를 볼 수 있습니다. "int result = k * k;" 문장을 보면 인라인 처리되었음을 알 수 있습니다.

   public static final void main() {
      int x = 4;
      int k = x;
      int result = k * k;
      String var = x + " * " + x + " = " + result;
      System.out.println(var);
   }

 

인라인 함수를 만드는 과정은 fun 앞에 키워드 inline만  붙이면 됩니다. 그런데 여기서 몇 가지 주의할 점이 있습니다.  인라인 함수는 이 함수에 전달되는 파라미터도 인라인으로 처리합니다. 전달되는 파라미터 중 일부를 인라인 처리하지 않으려면 어떻게 하면 될까요?  No inline, 즉 키워드 noinline을 사용하면 됩니다.

inline fun squared(a: Int, noinline f: (Int) -> (Int)): Int {
    return f(a)
}

역컴파일된 코드를 확인해 볼까요? 람다 함수를 호출하는 코드(f$iv.invoke(...))로 바뀌었습니다.

   public static final void main() {
      int x = 4;
      Function1 f$iv = MainKt::main$lambda$0;
      int result = ((Number)f$iv.invoke(Integer.valueOf(x))).intValue();
      String var = x + " * " + x + " = " + result;
      System.out.println(var);
   }

 

5장 배열과 컬렉션에서 소개하는 표준 라이브러리 함수는 성능을 향상시키기 위해 대부분 인라인 함수입니다. 또, 표준 라이브러리 함수는 메소드 체이닝(chaining) 기법으로 여러 함수를 연결해 사용합니다. 메소드 체이닝이란 앞의 함수에서 처리한 결과를 다음 함수에서 이어받아 처리하는 방식입니다. 아래 예에서 함수 filter()는 조건에 맞는 원소를 선택하는 역할이며, 함수 map()은 모든 원소에 공통 기능을 적용하는 역할입니다. 따라서, 함수 filter()는 인라인으로 처리하지 않고, 반면 함수 map()은 인라인으로 처리하는 게 효과적입니다. 

 

inline fun chainActions(
       	 	num: List<Int>,
    		noinline filter: (Int) -> Boolean, transform: (Int) -> Int) {
    val filtered = num.filter(filter)
    val transformed = filtered.map(transform)
    println(transformed)
}

fun main() {
    val numList = listOf(1, 2, 3, 4, 5)
    chainActions(numList, { it % 2 == 0 }, { it * it })
}

 

인라인 함수는 널-타입 파라미터를 전달 받을 수 있을까요? 인라인 함수에서 널-타입 파라미터를 전달받으려면 어떻게 하면 될까요? 앞에서처럼 해당 파라미터에 키워드 noinline 을 붙이면 됩니다.

inline fun findNumber(arr: Array<Int>,
                      noinline op: ((Int) -> Boolean)?): Int {
    if (op == null) return -1
    for (element in arr) {
        if (op(element)) return element
    }
    return -1
}

fun main() {
    val num:Array<Int> = arrayOf(1, 2, 3, 4, 5)
    var action: ((Int) -> Boolean)? = { it % 2 == 0 }
    println(findNumber(num, action))
}