Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using star projection as a supertype in Kotlin

Can anyone help me explain what's going on here?

private val map1 = mutableMapOf<String, Data<State<*>>>()
private val map2 = mutableMapOf<String, Data<*>>()

init {
    map1.put("1", Data<State<String>>()) //it does not work
    map2.put("2", Data<State<String>>()) //it works
    map2.put("3", Data<State<Int>>()) //it works
}

class Data<T>
class State<T>

I read from Kotlin docs that if the type is unknown, you can use star projection(*) and then later use any type. So why doesn't it work for the first case? It says Type Mismatch Error.

like image 244
honey_ramgarhia Avatar asked Apr 13 '19 05:04

honey_ramgarhia


2 Answers

Data<*> is the common supertype of Data<String>, Data<Any>, Data<AnythingYouPutThere>. But Data<State<*>> is not a common supertype of Data<State<String>> etc.; it's Data with a specific type parameter State<*> (which is the supertype of State<String> etc.)

Unfortunately, Kotlin doesn't support general existential types as Scala does, but in those terms Data<State<*>> is Data<State<T> forSome T> while you want Data<State<T>> forSome T.

I set mutableMapOf<String, Data<State<*>>>() to mutableMapOf<String, Data<out State<*>>>() and it worked. I don't know why

Data<out State<*>> allows any subtype of State<*> as type parameter of Data. So it can also be expressed as an existential type: Data<T> forSome T : State<*>, which isn't quite Data<State<T>> forSome T, because State<*> can have subtypes which aren't State<Something>. For example, if you have class State2 extends State<Int>(), Data<out State<*>> allows Data<State2>, but Data<State<T>> forSome T wouldn't.

like image 199
Alexey Romanov Avatar answered Sep 29 '22 08:09

Alexey Romanov


Check out the docs here on projection and declaration variance

It says that out is required to tell the compiler the type of the <>

like image 45
Nick Mowen Avatar answered Sep 29 '22 07:09

Nick Mowen