본문 바로가기

프로그래밍/kotlin

코틀린 - 인라인 함수

반응형

고차 함수를 사용하면 특정 런타임 패널티가 부과됩니다. 각 함수는 객체이며 클로저, 즉 함수 본문에서 액세스되는 변수를 캡처합니다. 메모리 할당 (함수 객체와 클래스 용)과 가상 호출은 런타임 오버 헤드를 초래합니다.

그러나 많은 경우에 이런 종류의 오버 헤드는 람다식을 인라인함으로써 제거 될 수 있습니다. 아래에 표시된 함수는이 상황의 좋은 예입니다. 즉,이 lock()기능은 전화 사이트에서 쉽게 인라인 될 수 있습니다. 다음과 같은 경우를 고려하십시오.

lock(l) { foo() }

매개 변수에 대한 함수 객체를 만들고 호출을 생성하는 대신 컴파일러에서 다음 코드를 생성 할 수 있습니다.

l.lock()
try {
    foo()
}
finally {
    l.unlock()
}

우리가 처음부터 원한 것이 아닌가?

컴파일러가이를 수행하게하려면 수정 자로 lock()함수 를 표시해야합니다 inline.

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

inline수정은 함수 자체에 전달 된 람다 모두에 영향을 미칩니다 : 그 모든 호출 사이트에 인라인됩니다.

인라인하면 생성 된 코드가 커질 수 있습니다. 그러나 합리적인 방법으로 (즉, 큰 기능을 인라인하지 않아도) 루프를 수행하면 성능면에서 효과가 있으며 특히 루프 내부의 "거대 (megamorphic)"호출 사이트에서 효과가 있습니다.

노 인라인

인라인 함수에 전달 된 람다 중 일부만 인라인되도록하려면 다음과 같이 함수 매개 변수 중 일부를 noinline수정 자로 표시 할 수 있습니다 .

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

Inlinable lambdas는 인라인 함수 안에서만 호출되거나 인라인 가능 인자로 전달 noinline될 수 있지만, 우리는 우리가 좋아하는 어떤 방식 으로든 조작 될 수 있습니다 : 필드에 저장되고, 전달됩니다.

인라인 함수에 인라인 가능 함수 매개 변수가없고 구체화 된 유형 매개 변수가없는 경우 컴파일러에서 해당 함수를 인라인하면 매우 유용하지 않으므로 컴파일러에서 경고를 표시합니다 (인라인 함수를 사용하여 인라인이 필요하다고 확신하는 경우 경고를 표시하지 않을 수 있습니다). 주석 @Suppress("NOTHING_TO_INLINE")).

비-로컬 리턴

코틀린에서는 return명명 되지 않은 함수 나 익명의 함수를 종료하기 위해 정규화되지 않은 정규화 된 함수 만 사용할 수 있습니다 즉, 람다를 종료하려면 레이블 을 사용해야하고 람다에서는 베어 return함수를 반환 할 수 없기 때문에 람다 안에서 베어 를 금지합니다.

fun foo() {
    ordinaryFunction {
        return // 에러: 'foo'를 리턴할 수 없습니다.
    }
}

그러나 lambda가 전달 된 함수가 인라인되면, 리턴도 인라인 될 수 있으므로 허용됩니다.

fun foo() {
    inlineFunction {
        return // OK: 람다도 인라인 가능합니다.
    }
}

이러한 반환 값 (람다에 있지만 둘러싸는 함수를 빠져 나옴)을 비 로컬 리턴 이라고 합니다. 우리는 인라인 함수가 자주 묶는 이러한 종류의 루프에 익숙합니다.

fun hasZeros(ints: List<Int>): Boolean {
    ints.forEach {
        if (it == 0) return true // hasZoros에서 리턴합니다.
    }
    return false
}

일부 인라인 함수는 매개 변수로 전달 된 lambdas를 함수 본문에서 직접 호출하지 않고 로컬 객체 나 중첩 함수와 같은 다른 실행 컨텍스트에서 호출 할 수 있습니다. 이러한 경우, 람다에서는 비 로컬 제어 흐름도 허용되지 않습니다. 이를 나타 내기 위해, lambda 매개 변수를 crossinline수정 자로 표시해야합니다 .

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

break및 continue인라인 람다에서 아직 사용할 수 없습니다, 그러나 우리는 그들도 지원할 계획이다.

구체화 된 유형 매개 변수

때때로 우리에게 전달 된 타입을 매개 변수로 접근해야한다 :

fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
    var p = parent
    while (p != null && !clazz.isInstance(p)) {
        p = p.parent
    }
    @Suppress("UNCHECKED_CAST")
    return p as T?
}

여기서는 트리를 걸어 리플렉션을 사용하여 노드에 특정 유형이 있는지 확인합니다. 괜찮아요.하지만 전화 사이트는별로 예쁘지 않습니다.

treeNode.findParentOfType(MyTreeNode::class.java)

우리가 실제로 원하는 것은이 함수에 형식을 전달하는 것입니다. 즉, 다음과 같이 호출하십시오.

treeNode.findParentOfType<MyTreeNode>()

이를 가능하게하기 위해 인라인 함수는 구체화 된 타입 매개 변수를 지원 하므로 다음과 같이 작성할 수 있습니다 :

inline fun <reified T> TreeNode.findParentOfType(): T? {
    var p = parent
    while (p != null && p !is T) {
        p = p.parent
    }
    return p as T?
}

type 매개 변수를 reified수식어로 수식 했으므로 이제 함수 내부에서 일반 클래스처럼 액세스 할 수 있습니다. 함수가 인라인되어 있기 때문에, 더 반사는 정상처럼 사업자, 필요하지 않습니다 !is 그리고 as지금 노력하고 있습니다. 또한 위에서 언급 한대로 호출 할 수 있습니다 myTree.findParentOfType<MyTreeNodeType>().

많은 경우 리플렉션이 필요하지 않을 수 있지만 구체화 된 유형 매개 변수로 리플렉션을 사용할 수 있습니다.

inline fun <reified T> membersOf() = T::class.members

fun main(s: Array<String>) {
    println(membersOf<StringBuilder>().joinToString("\n"))
}

일반 함수 (인라인으로 표시되지 않음)는 매개 변수를 구체화 할 수 없습니다. 런타임 표현이없는 유형 (예 : 비 구체화 된 유형 매개 변수 또는 가상의 유형 Nothing)은 구체화 된 유형 매개 변수에 대한 인수로 사용할 수 없습니다.

낮은 수준의 설명은 사양 문서를 참조하십시오 .

인라인 속성 (1.1 이후)

inline수정은 백업 필드가없는 속성의 접근에 사용할 수 있습니다. 개별 속성 접근 자에 주석을 달 수 있습니다.

val foo: Foo
    inline get() = Foo()

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

모든 접근자를 인라인으로 표시하는 전체 속성에 주석을 달 수도 있습니다.

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

호출 사이트에서 인라인 접근자는 일반 인라인 함수로 인라인됩니다.

공용 API 인라인 함수의 제한 사항

인라인 함수가 public또는 또는 선언 protected의 일부가 아닌 인라인 함수는 모듈 의 공용 API로 간주 됩니다. 다른 모듈에서 호출 할 수 있으며 이러한 호출 사이트에서도 인라인됩니다.privateinternal

이것은 호출 모듈이 변경 후에 다시 컴파일되지 않을 경우 인라인 함수를 선언하는 모듈의 변경으로 인해 발생하는 특정 이진 비 호환성 위험을 부과합니다.

이러한 호환성이의 변화에 의해 도입되는 위험 제거하기 위해  모듈의 -public API를 공개 API의 인라인 함수는 비공개-API 선언, 즉 사용 할 수 없습니다 private와 internal자신의 몸에, 선언과 부품.

internal로 주석 할 수있다 선언 @PublishedApi공개 API의 인라인 함수에서 사용을 할 수 있습니다. 때 internal인라인 함수로 표시되어 @PublishedApi그것이 공용 것처럼 몸 역시 확인된다.

반응형

'프로그래밍 > kotlin' 카테고리의 다른 글

코틀린 - 함수  (0) 2021.04.18
코틀린 - 고차 함수와 람다  (0) 2021.04.18
코틀린 - 코루틴  (0) 2021.04.18
코틀린 - 분리 선언  (0) 2021.04.18
컬렉션 : List, Set, Map  (0) 2021.04.18