I am reading and trying to understand Kotlin type projections, sometimes I come up with confusing things like this:
What does it mean to contravariant type to have a star projection and how does it come up to
I am reading and trying to understand Kotlin type projections, sometimes I come up with confusing things like this:
What does it mean to contravariant type to have a star projection and how does it come up to
This is also explained in the Kotlin documentation:
For
Foo<in T>
, whereT
is a contravariant type parameter,Foo<*>
is equivalent toFoo<in Nothing>
. It means there is nothing you can write toFoo<*>
in a safe way whenT
is unknown.
We have a class Foo<T>
with contravariant T
(declaration-site), i.e. Foo
only works as a consumer of T
:
class Foo<in T> {
fun accept(t: T) {
println(t)
}
}
We can use this type in simple generic functions as follows:
fun <F> usingFoo(con: Foo<F>, t: F) {
con.accept(t)
}
(used F
in order to distinguish from class type parameter T
)
This works fine since we use Foo
as intended: As a consumer of T
s.
Now, your quote simply says that having a parameter of type con: Foo<*>
instead of con: Foo<T>
would have the following effect: "you can't call any methods that have T
in the signature".
Thus, the following fails:
fun <F> usingFoo(con: Foo<*>, t: F) {
con.accept(t) //not allowed!!
}
It's not safe to call accept
with an instance of F
because we don't know the type of Foo
(denoted by star projection).
Star Projection: Sometimes you want to say that you know nothing about the type argument, but still want to use it in a safe way. The safe way here is to define such a projection of the generic type, that every concrete instantiation of that generic type would be a subtype of that projection.