Kotlin inline, noinline, crossinline

박채빈·2025년 10월 21일

KotlinStudy

목록 보기
9/9

개요

Kotlin에서 inline, noinline, crossline 한정자는 고차 함수(higher-order functions)의 성능을 최적화하고 람다(lambda)의 동작을 제어하기 위해 사용한다.

핵심 개념은 inline이며, noinline, crosslineinline 함수 내의 람다 파라미터를 제어하는 용도로 사용한다.

inline

inline은 함수를 호출하는 지점에 함수 본문 코드를 그대로 복사해 붙여 넣도록 컴파일러에게 지시하는 한정자이다.

사용 이유

Kotlin에서 람다를 포함한 고차 함수를 사용하면, 일반 함수 호출보다 약간의 성능 비용(overhead)이 발생한다.

  1. 객체 생성 : 람다는 컴파일 시 익명 클래스(anonymous class)의 인스턴스, 즉 객체로 생성된다.
  2. 메모리 할당 : 이 객체를 저장하기 위한 메모리(heap) 할당이 필요하다.
  3. 가상 메서드 호출 : 람다를 실행하는 것은 이 객체의 메서드를 호출하는 것이라 간접적인 함수 호출(virtual call) 비용이 발생한다.

이 비용은 미미하지만, forEach처럼 반복문 안에서 수백, 수천 번 호출되면 성능에 영향을 준다.
inline을 사용하면 컴파일러가 함수 본문과 람다의 코드를 호출 지점에 직접 삽입한다. 그 결과, 함수 호출이나 람다 객체 생성이 아예 사라져 성능이 향상된다.

특징

비-로컬 반환 (Non-local return)
inline함수에 전달된 람다 내에서는 return 키워드를 사용할 수 있다. 이 때 return은 람다 자신만 종료하는 것이 아니라, 그 람다를 호출한 바깥쪽 함수까지 종료시킨다. 이를 "비-로컬 반환" 이라고 한다.

예제: inline 사용

// 1. 일반 고차 함수 (inline 아님)
fun runNormal(block: () -> Unit) {
    println("Before block")
    block() // 람다 객체 생성 및 메서드 호출 발생
    println("After block")
}

// 2. inline 함수
inline fun runInline(block: () -> Unit) {
    println("Before block")
    block() // block의 코드가 이 자리에 복사됨
    println("After block")
}

fun main() {
    // runInline 호출
    runInline {
        println("Inlined action")
        // 비-로컬 반환: main 함수가 여기서 종료됨
        return 
    }
    
    // 이 코드는 실행되지 않음
    println("End of main") 
}

컴파일 후 예상

fun main() {
    // --- runInline { ... } 시작 ---
    println("Before block")
    // --- 람다 본문 시작 ---
    println("Inlined action")
    return // main 함수가 여기서 종료
    // --- 람다 본문 끝 ---
    // println("After block") // 'return' 때문에 도달 불가
    // --- runInline { ... } 끝 ---
    
    // println("End of main") // 'return' 때문에 도달 불가
}

runInline 함수 호출이 사라지고, 그 내용과 람다의 내용이 main함수에 직접 삽입된다.


noinline

noinlineinline 함수의 파라미터로 전달된 람다 중 특정 람다만 inline되는 것을 방지할 때 사용한다.

사용 이유

inline 함수는 모든 람다 파라미터를 기본적으로 인라인하려고 한다. 하지만 람다를 인라인하지 않고 객체로서 저장하거나 다른 곳에 전달해야 할 필요가 있을 수 있다.

  • 람다를 변수에 저장해야 할 때
  • 람다를 다른 함수(inline이 아닌)의 인자로 전달해야 할 때

이 경우, 해당 람다 파라미터 앞에 noinline을 붙여 "이것은 인라인하지 말고, 일반 람다 객체로 만들어 줘" 라고 컴파일러에 알린다.

예제 : noinline 사용
inline 함수가 두 개의 람다를 받지만, 하나는 인라인하고 다른 하나는 변수에 저장해야 하는 상황

inline fun processActions(
    inlineAction: () -> Unit,      // 이것은 인라인됨
    noinline noInlineAction: () -> Unit // 이것은 인라인되지 않음
) {
    println("Running inline action")
    inlineAction() // 코드가 여기에 복사됨

    println("Storing no-inline action")
    
    // noinline 람다는 객체이므로 변수에 저장하거나 다른 곳에 전달 가능
    val actionStore = noInlineAction 
    
    // 나중에 호출
    actionStore()
}

fun main() {
    processActions(
        inlineAction = {
            println("This is inlined")
            // return // OK: 비-로컬 반환 가능
        },
        noInlineAction = {
            println("This is NOT inlined")
            // return // Error! noinline 람다에서는 비-로컬 반환 불가
        }
    )
}

crossinline

crossinlineinline 함수의 람다 파라미터가 비-로컬 반환(return)을 사용하지 못하도록 금지하는 한정자이다.

사용 이유

inline 함수의 람다가 함수 본문이 아닌, 다른 실행 컨테스트에서 실행될때 비-오컬 반환을 사용하면 논리적 오류가 발생할 수 있다.

  • 람다가 다른 스레드에서 실행될 때
  • 람다가 Ruannable 같은 다른 객체 내부에서 실행될 때

예를 들어, 람다가 5초 뒤 다른 스레드에서 실행되도록 예약되었는데, 그 람다 안에 return이 있다고 하자. 이미 원래 함수는 종료되었을 수 있는데, 5초 뒤에 return하려는 시도는 위험하다.

예제 : crossinline 사용
람다를 즉시 실행하지 않고, Runnable 객체에 담아 실행하는 예제

inline fun runInDifferentContext(crossinline action: () -> Unit) {
    
    // 'action' 람다가 'Runnable'이라는 다른 객체(다른 컨텍스트) 내부에서 호출됨
    // 이 경우 'action'은 반드시 crossinline이어야 함
    val runnable = Runnable {
        println("Executing in different context")
        action() // 람다 실행
    }

    // 예시: 새 스레드에서 실행
    Thread(runnable).start()
    
    println("runInDifferentContext function finished")
}

fun main() {
    println("Main start")
    
    runInDifferentContext {
        println("My action is running")
        // return // Error! crossinline 람다에서는 비-로컬 반환 불가
    }
    
    println("Main end")
    Thread.sleep(100) // 데모를 위해 잠시 대기
}

/*
출력 결과 예상 (순서는 스레드 스케줄링에 따라 다름):
Main start
runInDifferentContext function finished
Main end
Executing in different context
My action is running
*/

main 함수가 먼저 종료된 후에도 crossline 람다가 다른 스레드에서 안전하게 실행된다.

한정자적용 대상주요 목적비-로컬 반환 (Non-local return)
inline고차 함수성능 최적화 (객체 생성 및 함수 호출 제거)허용 (O)
noinlineinline 함수의 람다 파라미터특정 람다의 인라인 방지 (객체로 저장/전달)금지 (X)
crossinlineinline 함수의 람다 파라미터다른 컨텍스트에서 람다를 안전하게 실행 (인라인은 유지)금지 (X)
profile
안드로이드 개발자

0개의 댓글