1

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?

Adam Burley
  • 5,551
  • 4
  • 51
  • 72

1 Answers1

3

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.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • Great, the underscore was the missing piece I didn't know about, I've learned something new there. For the `ID` type parameter it's used inside the method's body in the implementation, but is inferred based the method which that method is calling. I thought maybe in that case you have to add the type parameter to the method in Java but this is a level of complexity of generics beyond what I ever did in Java. If you are interested: https://github.com/j256/ormlite-core/blob/a2c9633b8ecdf2c3f9e7581c251e5795c97143b3/src/main/java/com/j256/ormlite/table/TableUtils.java#L215 – Adam Burley Nov 29 '22 at 22:57
  • 1
    Seems like this method could create a `TableInfo` so it doesn’t have to expose the invented and unused `ID` type. – Tenfour04 Nov 30 '22 at 04:12