Kotlin syntax for inferring generic supertype from subtype

981 views Asked by At

Trying to call existing Java code that expects a Class as a parameter, I tried code along the lines of this in Kotlin:

package com.example

//Acutally imported Java code in someone elses's library
abstract class JavaCode<T> {
    fun doTheThing(thing: Class<JavaCode<T>>) {
        //Does work
    }
}

//My code
class KotlinCode : JavaCode<String>() {
    fun startTheThing() {
        doTheThing(KotlinCode::class.java)
    }                             // ^ Type inference failed. Expected type mismatch
}

But that does not compile with the following error:

Type inference failed. Expected type mismatch: inferred type is Class<KotlinCode> but Class<JavaCode<String>> was expected

So I tried to force a cast (as suggested in this answer):

hello(GenericArgument::class.java as Class<Callable<String>>)

But that has a warning:

Unchecked cast: Class<KotlinCode> to Class<JavaCode<String>>

So what is the correct syntax to use? Is this related?

1

There are 1 answers

6
Joshua On BEST ANSWER

There are several problems in your code.

First, Callable<String?> is no equal to Callable<String>. Callable<String?> means the argument can be String or null but Callable<String> is String only.

Second, Class<GenericArgument> does not implement Class<Callable<String>> but GenericArgument implements Callable<String>. they are different. You can change it to use generic instead.

private fun <T : Callable<String>> hello(callable: Class<T>) {
    System.out.println(callable.toString())
}

Now, the generic parameter is bound by Callable<String>.

Third, callable.toString() probably does not do what you want. callable.toString() will call the toString() of the class instead of object, e.g. class com.example.yourclass. If you want to call the object toString(). This is the correct one.

override fun call(): String {
    hello(GenericArgument())
    return "value"
}

private fun <T : Callable<String>> hello(callable: T) {
    System.out.println(callable.toString())
}

Moreover, Kotlin allows to pass function as parameter or use SAM for interface. Implementing for Callable is not necessary.

Edit: As op updated the question.

@Suppress("UNCHECKED_CAST")
fun <T, U : JavaCode<T>> JavaCode<T>.doTheThing2(thing: Class<U>) {
    doTheThing(thing as Class<JavaCode<T>>)
}