자바에서 호출 못하는 코틀린 함수 만들기

Jaychy·2021년 6월 21일
1

알아가는 것

목록 보기
2/11
post-thumbnail

Github 코틀린으로 개발하면서 궁금했던 점에 대해 고찰하는 곳.

개요

일반적으로 코틀린은 자바와의 상호운용성을 기본으로하기 때문에
자바 코드에서 코틀린 코드를 호출하지 못하는 것은 말도 안 되는 일입니다.

하지만 런타임에서 타입이 소거되는 제네릭의 특성을 우회하여 만든
inline refied function을 이용하면
자바에서 호출하지 못하는 코틀린 함수를 만들 수 있습니다.

일단 만들어보자

코틀린에는 Iterable<*>.filterIsInstance() 라는 확장 함수가 있습니다.
이 함수는 위에서 소개한 inline refied function이며
타입 소거를 하지 않도록 우회한 코틀린의 reify 기능을 이용하여 구현하였습니다.

구현에 대한 자세한 설명은 밑에서 하겠습니다.

다음 함수는 filterIsInstance() 함수를 똑같이 따라한 함수입니다.

// A.kt
inline fun <reified T> filterIsInstance(list: List<*>): List<T> {
    val destination = mutableListOf<T>()
    for (element in list) {
        if (element is T) {
            destination.add(element)
        }
    }
    return destination
}

이렇게 만든 함수는 코틀린에서는 정상적으로 작동합니다.

val anyList = listOf(1, "2", '3')

filterIsInstance<Int>(anyList)    // List<Int>
    .forEach { println(it) }      // 1
filterIsInstance<String>(anyList) // List<String>
    .forEach { println(it) }      // 2
filterIsInstance<Char>(anyList)   // List<Char>
    .forEach { println(it) }      // 3

하지만 자바로 함수를 호출하려고 하면 IDE에서 함수 이름도 띄워주지 않습니다.

AKt.filterIsInstance<Integer>(anyList);	// Compile Error

왜 그럴까?

자바의 제네릭은 컴파일 시점이 지나서 런타임 시점에서는 타입이 소거되어 작동합니다.
물론 코틀린도 동일하게 작동합니다.

하지만 코틀린에서는 inline 함수와 reify(실체화된 타입 사용) 기능을 이용하여
컴파일 시점에 타입이 소거되는 것을 막을 수 있습니다.

왜 inline 함수여야만 하나요?

일단 제네릭은 컴파일 시점에 타입이 소거되도록 설계되었기 때문에
코틀린이라고 해서 이를 막을 수 있는 것은 아닙니다.

하지만 인라인 함수는 인라인 함수를 호출한 곳에 인라인 함수의 본문을
그대로 바이트코드로 치환합니다.

이런 특성을 이용하여 바이트코드로 치환할 때 제네릭의 정보를
그대로 코드에 작성하도록 하게 하면 됩니다.
위 filterIsInstance() 메소드에서 인라인을 적용하면 다음과 같이 변경됩니다.

val anyList = listOf(1, "2", '3')

val destination = mutableListOf<String`>`()
for (element in anyList) {
    if (element is String) {
        destination.add(element)
    }
}
destination.forEach { println(it) }

이렇게 인라인 함수인 경우에만 타입 정보가 살아있는 것을 알 수 있습니다.

결론

inline하고 reified한 함수는 자바 코드에서 호출할 수 없습니다.

profile
아름다운 코드를 꿈꾸는 백엔드 주니어 개발자입니다.

0개의 댓글