1

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?

Alex Taylor
  • 8,343
  • 4
  • 25
  • 40

1 Answers1

0

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>>)
}
Joshua
  • 5,901
  • 2
  • 32
  • 52
  • I understand there is a difference between `String` and `String?`. After that, perhaps my example breaks down a little. It's actually an abstract Java class that I'm extending. The method I want to use is on the base class. That is someone else's library, making changes difficult. It's not actually a `Callable`; that was just to provide an example with the same problem. I also realise that `callable.toString()` isn't particularly useful. Once again, that was just to prove the point. It sounds like I have to change the callee (`hello`), but the caller (`call`) is the bit I can modify. – Alex Taylor Sep 18 '17 at 04:13
  • @AlexTaylor Java does not have generic metadata at runtime so generic cast can result in error. – Joshua Sep 18 '17 at 04:20
  • And Kotlin provides no ability to guard against it? Basically, the Java code couldn't provide runtime type safety in this situation, but Kotlin gives you a warning? I've updated the code with something closer to my real code. – Alex Taylor Sep 18 '17 at 04:26
  • @AlexTaylor This is the JVM limitation not Java code. Kotlin compile its code to run on JVM. Kotlin give you a warning because it cannot be checked. I will have to look at your code later as I am going out for lunch. – Joshua Sep 18 '17 at 04:29
  • @AlexTaylor See my edit. After your update, your example only solution is force cast. But you can improve safety by implement a Kotlin function to do the force cast instead of everywhere that you call the method. – Joshua Sep 18 '17 at 05:25
  • 1
    OK, so we separate out the cast as a function so the suppression only affects the problem code. Got it. – Alex Taylor Sep 18 '17 at 05:44