본문 바로가기

Kotlin

Kotlin - Inline functions

https://kotlinlang.org/docs/inline-functions.html

 

Inline functions | Kotlin

 

kotlinlang.org

고차함수를 사용하면 모든 함수가 개별 객체로 생성되고, 클로저가 발생하는 등의 런타임시의 단점들이 발생한다. 이는 람다 표현식을 인라인 (컴파일시에 함수 호출 코드를 함수 본문으로 대체하는 방법)으로 해결 할 수 있다.

inline fun <T> lock(lock: Lock, body: () -> T): T { ... }

인라인 함수는 아래 처럼 함수에 "inline" 수정자를 붙이면 되고, 컴파일 시 inline함수를 호출하는 모든곳에  함수 본문이 인라인 된다. 인라인 대상은 인라인 함수 본문 뿐만 아니라 매개변수로 전달되는 람다식 마저 인라인 한다.

noninline

inline 함수의 모든 람다 매개변수는 모두 inline 된다. 이중 일부를 inline 하고 싶지 않다면 noinline 수정자를 붙여주면 된다. 

inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { ... }

Non-local returns

일반 함수나 익명함수에서의 return 문은 상관 없지만 람다에서의 return 문은 해당 람다를 포함하고 있는 함수에 대한 return 을 의미하기 때문에 사용할 수 없다. 사용하고 싶으면 qualified return를 사용해야 한다. 하지만 람다가 inline 함수에 사용되면 해당 람다식의 함수 본문이 inline 되기 때문에 return문을 사용해도 관계 없다 (이때 return 은 inline을 포함하는 함수 의 return 으로 동작한다.) 이 리턴을 non-local return 이라고 한다.

어떤경우에는 람다의 non-local return 을 금지하고 싶은 경우가 있다. 어떤 함수가 외부에서 주입받은 람다에 의해 의도치 않게 종료되는것을 막고싶은 경우가 있을거고, 그런 경우에는 파라미터 에 crossline 수정자를 붙여주면 된다.

inline fun f(crossinline body: () -> Unit) {
    val f = object: Runnable {
        override fun run() = body()
    }
    // ...
}

Reified type parameters

종종 파라미터로 타입을 전달해서 처리하는 경우들이 있다. 아래와 같이 처리 할 수 있는데 사용 방법이 그렇게 좋아보이지는 않는다.

// clazz라는 이름으로 타입을 전달받아서 해당 타입의 결과를 반환한다.
fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
	...
}

// 함수를 사용할 때는 아래와 같이 사용한다.
treeNode.findParentOfType(MyTreeNode::class.java)

조금더 예쁘게 사용하기 위해서 inline 함수에서 지원하는 refied type parameter를 사용할 수 있다. 사용방법은 아래와 같이 (제네릭을 선언하듯이) <refied T> 를 이용해 타입 변수를 지정하고 "함수명<T>()" 형태로 함수를 호출하면된다. (제네릭을 사용 하듯이)

// T 라는 이름으로 타입 변수를 지정한다.
inline fun <reified T> TreeNode.findParentOfType(): T? {
	...
}

// 제네릭을 사용하듯이 아래처럼 함수를 호출 할 수 있다.
treeNode.findParentOfType<MyTreeNode>()

Inline properties

inline 수정자는 함수 뿐만 아니라 속성에도 사용할 수 있다. (backing field는 불가능 하다.) 속성의 개별 accessor에 붙일 수도 있고, 속성 에 붙일 수도 있다.

// 개별 accessor 에 inline을 붙인 경우
val foo: Foo
    inline get() = Foo()

var bar: Bar
    get() = ...
    inline set(v) { ... }

// 속성 전체에 inline을 붙인 경우
inline var bar: Bar
    get() = ...
    set(v) { ... }