Kotlin check if elements in list with erased type are instances of a Java class

Viewed 152

I have a class that stores a resultClass and I want to do some type checking and make sure users get a type safe list when calling the getTypeSafeResults function.

class MyClass<T>(
    private val resultClass: Class<T>
){
    fun getTypeSafeResults() : List<T> {
        val results: List<Any?> = remoteFetchResults()
        return results.filterIsInstance(resultClass)
    }
}

This API must be Java friendly, hence why resultClass is a Java class.

But, when resultClass=Integer::class.java / resultClass=Long::class.java, and the results contain the Kotlin version of these e.g. kotlin.Int / kotlin.Long, they do not match the filterIsInstance(resultClass).

Is there is a way to manually check if the resultClass is the Java version of the Kotlin type? I've tried along the lines of this snippet below but had no luck:

(When it is a kotlin Long, it.javaClass.kotlin="class kotlin.Long" but resultClass="long")

fun getTypeSafeResults(results: List<Any?>) : List<T> {
    return results.map {
        uncheckedCast(
            if (resultClass.isAssignableFrom(it.javaClass)) { // doesn't match kotlin long to java Long
                it 
            } else if (it.javaClass.kotlin == resultClass) { // doesn't match kotlin long to java Long
                it
            } else {
                throw Exception("Attempted to cast result of type ${it.javaClass} to $resultClass")
            }
        )
    }
}
1 Answers

I did some experiments to understand better what's going on.

All of these correctly find 42:

val list = listOf(42, 32L, "bob")
println(list.filter { it::class == Int::class })
println(list.filter { it::class == java.lang.Integer::class.java.kotlin })
println(list.filter { it::class == Int::class.java.kotlin })
println(list.filter { it.javaClass == Int::class.javaObjectType })
println(list.filter { it.javaClass == java.lang.Integer::class.java })

Only this doesn't:

println(list.filter { it.javaClass == Int::class.java })

What you can do is therefore ensure you're using the javaObjectType on the class the users give you:

class MyClass<T : Any>(
    resultClass: Class<T>
){
    private val effectiveClass = resultClass.kotlin.javaObjectType

    fun getTypeSafeResults() : List<T> {
        val results: List<Any?> = remoteFetchResults()
        return results.filterIsInstance(effectiveClass)
    }
}
Related