I have this in Java and somehow am not able to find the right way to specify the return type of getStuff()
in Kotlin.
class Base {
}
class Extended extends Base {
}
List<? extends Base> getStuff() {
return new ArrayList<Extended>();
}
List<Extended> foo = new ArrayList<>();
foo.addAll((List<Extended>)getStuff());
When I turn the getStuff()
function to Kotlin like so
fun getStuff(): List<Base> { ... }
I get a compiler error in Java at foo.addAll((List<Extended>)getStuff());
saying
List<Base> cannot be converted to List<Extended>
The only way I found, is to have getStuff() return List<*> but that seems not right.
CodePudding user response:
This seems to be a quirk resulting from how Kotlin supports declaration-site variance, but Java doesn't. The equivalent of <? extends Base>
in Kotlin is <out Base>
. In Kotlin, the read-only List interface is already <out T>
at the declaration site, so you usually don't need to specify out
when working with read-only Lists. But when a List is returned to Java code, Java can't see the declaration site variance so it thinks of it as an invariant T. You can mark the variance explicitly at the use-site:
fun getStuff(): List<out Base> { ... }
But Java still seems to see it as invariant from my experimentation. I think Kotlin is not bothering to apply use-site variance since it's redundant from the Kotlin side.
One way to hack around this in the Java side is to cast to covariant type and then cast to your other type:
List<Extended> foo = new ArrayList<>();
foo.addAll((List<Extended>)(List<? extends Base>)getStuff());
Or, you can change it to MutableList in Kotlin, such that it is not redundant to use use-site variance:
fun getStuff(): MutableList<out Base> { ... }
Either way, this is an unsafe cast, since it's possible the List returned by getStuff()
in Java or Kotlin could have elements that are subtypes of Base but are not Extended. This would explain why the Kotlin designers have not tried to "fix" the problem you ran into. It would only be enabling unsafe code.