일반적으로 코틀린은 자바와의 상호운용성을 기본으로하기 때문에
자바 코드에서 코틀린 코드를 호출하지 못하는 것은 말도 안 되는 일입니다.
하지만 런타임에서 타입이 소거되는 제네릭의 특성을 우회하여 만든
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
한 함수는 자바 코드에서 호출할 수 없습니다.