I’m having an interesting issue with Kotlin declaration-site variance.
Let’s say I have some interface with type parameter Foo
with method that takes generic param T as argument and return it, so T should be invariant.
Also, there is abstract class A
that implements Foo<List<String>>
, but doesn’t implement foo
directly, but have a different method with a similar signature.
interface Foo<T> {
fun foo(t: T): T
}
abstract class A : Foo<List<String>> {
//method foo is abstract
protected fun bar(list: List<String>): List<String> {
return listOf("a", "b")
}
}
The idea is that concrete child of A
will implement foo(List) but use bar
method of A
to get the value.
Then the interesting part: I can easily implement such class with Kotlin:
class B : A() {
override fun foo(strings: List<String>): List<String> {
return bar(strings)
}
}
This works fine, and code compiles.
But if I want to implement the same in Java,
public class C extends A {
@Override
public List<? extends String> foo(List<? extends String> strings) {
return bar(strings); // <- error here
}
}
there are two issues:
-
List<String>
becomesList<? extends String>
in argument and return types - implementation cannot call
bar(strings)
because it canot castList<? extends String>
toList<String>
.
As I understand that happens because List
is defined as List<out T>
in Kotlin, but becomes just java.util.List<T>
in java class.
How can this problem be (type-)safely resolved?
P.S. Java needs to be used because B
class is intended to be generated by annotation processor, so I can’t just convert it to kotlin.