ServiceLoader use with Kotlin interface having generic types


#1

I am facing some issue trying to port a part of my code to Kotlin. I will give a simplified version of the issue I am facing.

In Java, I can write this code which runs successfully (with compiler warning obviously) -

public interface MyException<T extends Exception> {
    void print(T ex);
}

public class MyRuntimeEx implements MyException<RuntimeException> {
    public void print(RuntimeException ex) {
        System.out.println(ex);
    }
}

public static void main(String[] args) {
    for (MyException exception : ServiceLoader.load(MyException.class)) {
        exception.print(new RuntimeException()); // Compiler warning, but runs successfully
    }
}

However, when I try writing the same in Kotlin, that compiler warning turns into an error and I cannot proceed with the code.

interface MyException<T : Exception> {
    fun print(ex: T)
}

class MyRuntimeEx : MyException<RuntimeException> {
    override fun print(ex: RuntimeException) {
        println(ex)
    }
}

fun main(args: Array<String>) {
    for (exception in ServiceLoader.load(MyException::class.java)) {
        exception.print(RuntimeException())  // Compiler error
    }
}

The error I am getting is "Out-projected type ‘MyException<*>!’ prohibits the use of ‘public abstract fun print(ex: T)’"
Is there something wrong with the way I have implemented? (I am using version 1.2.0-rc-39)


#2

This is because there is no guarantee that what will be returned will be a MyException<RuntimeException>, it could be a MyException<SomeOtherKindOfException>.
But you can cast it to MyException<RuntimeException>:

ServiceLoader.load(MyException::class.java) as MyException<RuntimeException>

The cast is unchecked, but I believe it will always succeed, since the type is erased.
It is unsafe, of course.


#3

Thanks for the quick reply but this requires me to know that the type is RuntimeException. The point is that I don’t know what (and how many) implementations might be present. As long as they all extend Exception, I should be able to call the print function.
The implementation of the interface is supposed to take care of how to achieve the functionality.


#4

Notice this is not a limitation of Kotlin, but a limitation of the JVM. Due to type erasure, there is no way to guarantee the implementation you receive will behave with the type you’re calling print with.

If you only need to make sure it will behave with any Exception, you might as well give up the type parameter and write the interface as:

public interface MyException {
    void print(Exception ex)
}

#5

Since there were multiple methods in the interface, I wanted to ensure all methods have the same parameter type -

void m1(Exception ex)
void m2(Exception ex)
void m3(Exception ex)

Here, an implementation can as well use different types for all the methods and it didn’t make sense to not use generics for such a case.


#6

No, you shouldn’t. Consider this class:

class MySomeOtherEx : MyException<SomeOtherKindOfException> {
    override fun print(ex: SomeOtherKindOfException) {
        println(ex)
    }
}

If you load it with ServiceLoader as MyException<*> and then call print function with an instance of RuntimeException it will blow in runtime.

So you have to know that particular implementation supports RuntimeException as an argument to be able to pass it to print.


#7

Thanks @ilya.gorbunov, understood the problem here.
Basically I got that working earlier because Java just throws a warning (which I had ignored). And the places where I was using the above code were having an indirect validation before the code reached that point, so such a scenario was never occurring.