I was told that it could be possible to read a contravariance if used "Any?" as in
fun processList(list: List<in Class1>) {
val item: Any? = list[0]
println(item)
}
To test out, I used:
open class Class2
open class Class1 : Class2()
fun main() {
val list2: List<Class2> =listOf(Class2(), Class2())
val list: List<in Class1> = list2
val item: Any? = list[0]
}
But I doubt this information because I get the error
Projection is conflicting with variance of the corresponding type parameter of 'kotlin.collections.List<in Class1>'. Remove the projection or replace it with '*'.
Is reading not possible at all? Besides, it suggests that I use "*", but why an unbounded wildcard would allow me to read? Shouldn't it have the same restrictions as both covariance and contravariance (not being able to produce nor consume)?
List<in Class1>
itself is simply not a valid type, let alone whether you can read from it.
The type parameter of List
is covariant:
interface List<out E> : Collection<E>
List<in Class1>
contradicts this variance, so is not allowed.
From the language spec,
Kotlin prohibits contradictory combinations of declaration- and use-site variance as follows.
- It is a compile-time error to use a covariant type argument in a contravariant type parameter
- It is a compile-time error to use a contravariant type argument in a covariant type parameter
There is no technical reason for this restriction. This is just to prevent you from doing writing code that doesn't make sense. By using Foo<in T>
, you are expressing that this Foo
should be a consumer of T
, but if the type parameter is covariant at the declaration site, that means Foo
has no way of consuming anything. Clearly, something has gone wrong, and the compiler error is there to tell you that.
If you replace List
with another type with a non-covariant type parameter, it works as expected. You can successfully get a Any?
from a MutableList<in Class1>
for example,
val list2: MutableList<Class2> = mutableListOf(Class2(), Class2())
val list: MutableList<in Class1> = list2
val item: Any? = list[0]
Star projection doesn't have both the restrictions of covariance and contravariance. Such a type would be rather useless, don't you think? It cannot consume anything nor produce anything (regarding its type parameter, at least).
The documentation clearly explains what a star projection means. To paraphrase, if the declaration site variance is invariant, *
acts like out
when you use it as a producer, and acts like in Nothing
when you use it as a consumer. Otherwise, *
will be equivalent to the declaration-site variance.
List
is covariant, so List<*>
just means out Any?
.