This works fine:
class Wrapped<out T>(val value: T)
open class Wrapper<T> {
fun wrap(map: T): Wrapped<T> = Wrapped(map)
}
class Wrapper2 : Wrapper<Map<String, String>>()
val wrapped: Wrapped<Map<String, String>> = Wrapper2().wrap(mapOf())
But, when I try to access Wrapper2.wrap
from Java, the Map comes back with a wildcard type:
Map<String, String> toWrap = new HashMap<>();
Wrapped<Map<String, String>> result;
result = new Wrapper<Map<String, String>>().wrap(toWrap); // ok
result = new Wrapper2().wrap(toWrap); // NOT ok, returns Wrapped<Map<String, ? extends String>>
I can work around this by overriding wrap
in Wrapper2
with the explicit type.
Why does Wrapper2.wrap
return a different type than Wrapper.wrap
?
You can suppress Kotlin using wildcards in generics as described in the Kotlin reference where it describes the @JvmSuppressWildcards
annotation (or the reverse of that @JvmWildcard
annotation).
From the docs:
On the other hand, if we don't need wildcards where they are generated, we can use @JvmSuppressWildcards
:
fun unboxBase(box: Box<@JvmSuppressWildcards Base>): Base = box.value
// is translated to
// Base unboxBase(Box<Base> box) { ... }
NOTE: @JvmSuppressWildcards
can be used not only on individual type arguments, but on entire declarations, such as functions or classes, causing all wildcards inside them to be suppressed.
Change
class Wrapper2 : Wrapper<Map<String, String>>()
to
class Wrapper2 : Wrapper<MutableMap<String, String>>()
You'll see in the Kotlin source,
public interface Map<K, out V> {
whereas:
public interface MutableMap<K, V> : Map<K, V> {
I believe out V
is the reason you're getting ? extends String
, see Covariance under the generics docs for Kotlin and a quick search on Google should give you some more insight into covariance and contravariance in Java .