faceoff
faceoff

Reputation: 191

Kotlin and Java PECS

So my goal is use in Kotlin similar construction to Java PECS:

List<? extends MyMarkerInterface> => MutableList<out MyMarkerInterface>

When Jackson set after compiling data to this variable(list) it's ok. When I try to add item from Kotlin code , Kotlin says that I can add only Nothing (type) items.

So how do I put inside List child of MyMarkerInterface in Kotlin?

Upvotes: 0

Views: 378

Answers (2)

gidds
gidds

Reputation: 18607

This is about variance.

(Because Kotlin distinguishes between mutable and immutable lists, it's stricter about this than Java, so you can't always compare directly.)

Suppose you have reference to a MutableList<out MyMarkerInterface>.  That parameter is the equivalent of Java's <? extends MyMarkerInterface>, and means you have a mutable list of MyMarkerInterface or some subtype.  But you don't know which subtype; it could be any type that implements your interface.

It could be a mutable list of MyImplementingClassA; so you clearly can't add an instance of MyImplementingClassB without risking violating its type-safety.  Nor vice versa.  In fact, without knowing any more about its type, it's not safe to add anything to it.  That's why Kotlin won't let you.  (The way it does that is by inferring the type Nothing, which is the ‘bottom’ type and has no values.)

However, if you want to get a value out of the list, you know it's some subtype of MyMarkerInterface, so you can happily treat it as a MyMarkerInterface reference.

That's why out variance means the list is a producer: it can produce values for you safely, but it can't consume values because no type would be safe.

And it's the exact opposite for in variance, which is the equivalent of Java's ? super.

(Which is of course the gist of PECS.  That exact phrasing only applies to Java — the Kotlin equivalent is ‘Producer Out, Consumer In’, which is probably too obvious to need an acronym! — but the principle's the same.)

The only safe way to be a both producer and a consumer is to be invariant: plain old MutableList<MyMarkerInterface>.  That way, you know that you can get MyMarkerInterface values out and put MyMarkerInterface values in.

Things are different for Lists, which are immutable in Kotlin.  (Or rather, you can't mutate them through that reference; you may be able to some other way.)  Because that reference won't let you put values in, a List is not acting as a consumer, only a producer, and so out variance is fine for it.

Your question doesn't give any detail, but you mentioned initialisation.  It's probably most common to create a List with all the values it will have up-front — either by calling listOf() or a constructor, or as the result of a map() or similar operation.

However, you could also create some type of MutableList, set it up as needed, and then upcast it to List (which is a superinterface of MutableList), e.g.:

val ml = ArrayList<MyImplementingClassA>()
// …some computation which calls ml.add()…
val l = ml as List<out MyMarkerInterface>

…though in practice, you generally wouldn't need that last line; Kotlin knows that Lists have out variance, and so it'd upcast ml to List<out MyMarkerInterface> automatically if needed.

Upvotes: 1

faceoff
faceoff

Reputation: 191

Remove out at all, and it works fine but without "? extends" when decompile code from bytecode after Kotlin

Upvotes: 0

Related Questions