Search code examples
kotlingenericskotlin-interop

How to resolve "Not enough information to infer type variable" when one type variable not inferrable AND using wildcard on the other?


Trying to convert some Java code to Kotlin, the Java code includes a call to a library method TableUtils.dropTable which is implemented in Java. The Java method signature of this method is

public static <T, ID> int dropTable(ConnectionSource connectionSource, Class<T> dataClass, boolean ignoreErrors) throws SQLException

When calling the method from Java it compiles fine even though the type variable ID is not known. For example:

public void method(ConnectionSource connectionSource, Class<? extends IRecordObject> clazz) {
    try {
        TableUtils.dropTable(connectionSource, clazz, true); // this compiles fine
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

After converting to Kotlin, the corresponding function fails to compile because the type variable ID cannot be inferred:

fun method(connectionSource: ConnectionSource?, clazz: Class<out IRecordObject>) {
    try {
        TableUtils.dropTable(connectionSource, clazz, true) // compile error: "Not enough information to infer type variable ID"
    } catch (e: SQLException) {
        e.printStackTrace()
    }
}

I don't know how I can explicitly specify the type variables, as one of them would be a wildcard, and you aren't allowed to use wildcards in type variables when calling functions. For example:

TableUtils.dropTable<out IRecordObject,Long>(connectionSource, clazz, true) // this also fails to compile, "Projections are not allowed on type arguments of functions and properties"

So how can I specify the type variable ID here to allow the code to compile in Kotlin?


Solution

  • The type ID is unused in the function signature, so it doesn't matter what it is. For the Kotlin version, you can literally put any type there to make the error go away. Whichever type you use will have no effect on the compiled code because of type erasure. You can use an underscore to allow T to be inferred.

    fun method(connectionSource: ConnectionSource?, clazz: Class<out IRecordObject>) {
        try {
            TableUtils.dropTable<_, Unit>(connectionSource, clazz, true)
        } catch (e: SQLException) {
            e.printStackTrace()
        }
    }
    

    I actually don't know how you can write out the type and make it work instead of using inference. The only way I can think to make it work is to make this function generic so you can use an invariant Class type:

    fun <T: IRecordObject> method(clazz: Class<T>) {
        JavaFoo.dropTable<T, Unit>(clazz, true)
    }
    

    I think the Java method signature should have used Class<? extends T> for more proper flexibility and probably should have omitted ID since it is effectively useless.