본문 바로가기

두두의 IT

[Kotlin] Inline 함수

728x90

1. 람다식을 사용했을 때 무의미한 객체 생성을 예방

//1. Kotlin
fun doSomethingElse(lambda: () -> Unit) {
    println("Doing something else")
    lambda()
}
//1. Java
public static final void doSomethingElse(Function0 lambda) {
    System.out.println("Doing something else");
    lambda.invoke();
}

 

inline 없을 때

//2. Kotlin
fun doSomething() {
    println("Before lambda")
    doSomethingElse {
        println("Inside lambda")
    }
    println("After lambda")
}
//2. Java
public static final void doSomething() {
    System.out.println("Before lambda");
    doSomethingElse(new Function() {
            public final void invoke() {
            System.out.println("Inside lambda");
        }
    });
    System.out.println("After lambda");
}

 

inline 있을 때

인라인 함수를 사용하게 되면 코드는 객체를 항상 새로 만드는것이 아니라 해당 함수의 내용을 호출한 함수에 넣는 방식으로 컴파일 코드를 작성하게 됩니다. 

//2. Kotlin(inline o)
inline fun doSomethingElse(lambda: () -> Unit) {
    println("Doing something else")
    lambda()
}
//1. Java
public static final void doSomething() {
    System.out.println("Before lambda");
    System.out.println("Doing something else");
    System.out.println("Inside lambda");
    System.out.println("After lambda");
}

==> Function 객체를 항상 호출했으나 이제는 내부에서 사용되는 2개의 함수가 내부 코드로 변환되어 사용되는것을 알 수 있었습니다. 이렇게 무의미하게 Function 객체를 항상 만들어내는 것이 없어졌습니다.

 

람다식에 로컬 변수 사용

//3. Kotlin
fun doSomething() {
    val greetings = "Hello"                // Local variable
    doSomethingElse {
        println("$greetings from lambda")  // Variable capture
    }
}
//3. Java
public static final void doSomething() {
    String greetings = "Hello";
    doSomethingElse(new Function(greetings) {
            public final void invoke() {
            System.out.println(this.$greetings + " from lambda");
        }
    });
}

람다식에서 사용하는 지역 변수는 아래와 같이 Function 객체의 생성자의 변수로 들어가는 것을 확인할 수 있습니다.

객체에 변수가 추가되었습니다. 즉, 객체의 메모리 사용량이 늘어났다는 것입니다.

이것을 볼때 이 경우 인라인 함수를 사용하면 좀 더 나은 성능을 보장할 수 있을 것이라는 사실을 알 수 있습니다.


2. reified 키워드

범용성 좋게 함수를 만들기 위해서 class Type을 이용할 수 있습니다. 아래와 같이 말이죠.

fun <T> doSomething(someValue: T)

하지만 이러한 class Type T 객체는 타입에 대한 정보가 런타임에서 Type Erase되어버려 알 수없어집니다. 그래서 아래와 같이 실행하면 에러가 발생하죠. 왜냐하면 타입을 알 수가 없기 때문입니다. 따라서 Class<T>를 함께 넘겨 type을 확인하고 casting 하는 과정을 거치곤합니다.

fun <T> doSomething(someValue: T, Class<T> type) { // runtime에서도 타입을 알 수 있게 Class<T> 넘김
    println("Doing something with value: $someValue")               // OK
    println("Doing something with type: ${T::class.simpleName}")    // Error
}

이러한 문제점에 때문에 reified 키워드를 사용하면 됩니다. 인라인(inline) 함수와 reified 키워드를 함께 사용하면 T type에 대해서 런타임에 접근할 수 있게 해줍니다. 따라서 타입을 유지하기 위해서 Class<T>와 같은 추가 파라미터를 넘길 필요가 없어집니다.

inline fun <reified T> doSomething(someValue: T) {
    println("Doing something with value: $someValue")               // OK
    println("Doing something with type: ${T::class.simpleName}")    // OK
}

3. inline 함수 단점

public inline 함수는 private 함수를 호출할 수 없음

아래의 코드처럼 코드를 작성하신다면 Public-API inline function cannot access non-public API fun라는 에러가 남

inline fun doSomething() {
    doItPrivately()  // Error
}

private fun doItPrivately() { }

'두두의 IT' 카테고리의 다른 글

[Kotlin] Infix 함수  (0) 2022.04.12
[Kotlin] 가변인자 vararg(Variable number of arguments)  (0) 2022.04.12
[Kotlin] data Class  (0) 2022.04.12
[Kotlin] Modifiers  (0) 2022.04.12
객체지향 프로그래밍이란?  (0) 2022.04.12