While reading about Kotlin generics type variance and projection I came up with completely unfamiliar concept to me. Can someone explain please what is the idea that the author wants to explain? Please this quote from Kotlin in Action, MEAP:
There is no sense to get anout
projection of a type parameter that already hasout
variance, such asList<out T>
. That would mean the same asList<T>
, becauseList
is declared asclass List<out T>
. The Kotlin compiler will warn that such a projection is redundant.
There are two concrete questions here:
As quoted, it would be “redundant”, thus you’d never want to do that. It simply does not add any benefit. The whole quote is related to use-site variance, i.e. variance specified by the client (corresponds to wildcards in Java). The class List<out T>
already has a declaration-site variance modifier out
, which makes the use-site application of out
redundant.
Here's an example of redundant client-site variance:
fun <T> useList(list: List<out T>) {
println("first element: ${list[0]}")
}
I applied the out
modifier to a function parameter of type List<T>
, this is an example of use-site variance. It is redundant which the compiler notices: "Projection is redundant"
. It does not make it worse nor any better.
On the other hand, if you're using a type that is not projected on declaration-site already, it does make sense. For example, the Array
class is not restricted in its variance: public class Array<T>
.
Rewriting the former example to use Array
, it suddenly makes sense to add an out
modifier, since the parameter is only used as a producer of T
, i.e. not in in
position.
Example of meaningful client-site variance:
fun <T> useArray(arr: Array<out T>) {
println("first element: ${arr[0]}")
}
As a rule of thumb, corresponding to Java‘s PECS (Producer extends
, Consumer super
), you can memorize POCI for Kotlin (Producer out
, Consumer in
).
- Why you ever need to add out projection on already out projected type List?
You don't, but it may happen by accident, such as through refactoring or in a deep call chain where you lose sight of the generic parameters being passed around.
- Even if you do so, how you get the same List?
In an analogy, you might consider the projection as an idempotent transform: out out T
is the same as just out T
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With