Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin projection redundancy

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 an out projection of a type parameter that already has out variance, such as List<out T>. That would mean the same as List<T>, because List is declared as class List<out T>. The Kotlin compiler will warn that such a projection is redundant.

There are two concrete questions here:

  1. Why you ever need to add out projection on already out projected type List?
  2. Even if you do so, how you get the same List?
like image 318
sunflower20 Avatar asked Jan 09 '18 12:01

sunflower20


2 Answers

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]}")
}

Rule of Thumb

As a rule of thumb, corresponding to Java‘s PECS (Producer extends, Consumer super), you can memorize POCI for Kotlin (Producer out, Consumer in).

like image 158
s1m0nw1 Avatar answered Oct 13 '22 06:10

s1m0nw1


  1. 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.

  1. 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.

like image 4
Marko Topolnik Avatar answered Oct 13 '22 06:10

Marko Topolnik