4

I was reading about reasons why kotlin does not have wildcards (https://kotlinlang.org/docs/reference/generics.html). It all came to the declaration-site variance. We have <in T> and <out T> constructions which should replace wildcards. I think I understood how <out T> works but I have troubles with <in T>.
So in java we could write something like this:

public List<? extends Number> list1;
public List<? super String> list2;

First case after initialization becomes read only list (though not perfectly immutable cause we could clear it) which could be read if we treat every element as Number.
Second case is write only (though we could read it if we treat every element as Object). We could write there String and it subclasses.
In Kotlin I was able to recreate list1 example using <out T> like this:

class Service {
  val container = Container(mutableListOf("1", "2", "3"))
}
class Container<T>(var list1: MutableList<out T>)

Finally I tried something similar with <in T> thinking that I could recreate list2 example, but I failed: enter image description here

Can someone explain to me how to achieve my list2 example in Kotlin? How should I use <in T> construction in proper way?

Yogesh Umesh Vaity
  • 41,009
  • 21
  • 145
  • 105
pokemzok
  • 1,659
  • 1
  • 19
  • 29

1 Answers1

7

Kotlin List<E> is not equivalent to Java List<E>. The Java list has the mutating functions, while the Kotlin list is read-only. It's Kotlin MutableList<E> that is equivalent to the Java list.

Next, take a look at the List<E> declaration: its type parameter is covariant (out E), and the declaration-site variance cannot be overridden by use-site variance, that's why you cannot have a List<in T>.

Moreover, the declaration-site variance out E means that E never appears in an in-position (there's no function parameter of type E and no mutable property of type E), and indeed, since List<E> is read-only, it doesn't take E into any of its functions (*).

You can convert your example to use MutableList<E> instead:

class Container2<T>(var list2: MutableList<in T>)

The MutableList<E> interface has its E invariant, and the in-projection at use-site does not conflict with declaration-site variance.

(*) Actually, it does, but only using the parameters that are marked with the @UnsafeVariance annotation, which simply suppresses the variance conflict, more about it can be found here.


Also, a small remark:

It all came to the declaration-site variance. We have <in T> and <out T> constructions which should replace wildcards.

It's actually use-site variance that replaces Java wildcards. Declaration-site variance is only applied at the type parameter declaration (where the type is defined), and when it is, all the usages of the type will have that variance (e.g. when you use List<CharSequence>, it's actually a List<out CharSequence> because of the declaration-site variance of List<out E>). And use-site variance is, accordingly, for particular usages of a generic type.

hotkey
  • 140,743
  • 39
  • 371
  • 326