Использование звездной проекции в качестве супертипа в Kotlin

#android #generics #kotlin

#Android #дженерики #kotlin

Вопрос:

Кто-нибудь может помочь мне объяснить, что здесь происходит?

 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>
  

Я прочитал из документов Kotlin, что если тип неизвестен, вы можете использовать звездную проекцию (*), а затем использовать любой тип. Так почему же это не работает в первом случае? В нем говорится Type Mismatch Error .

Комментарии:

1. Это может быть глупой идеей, но попытка изменить T в состоянии на другую букву

2. Я установил mutableMapOf<String, Data<State<*>>>() в значение mutableMapOf<String, Data<out State<*>>>() и это сработало. Я не знаю почему. @NickMowen

3. Примечание: я отредактировал свой ответ, чтобы уточнить, как Data<out State<*>> он относится к экзистенциальным типам. Создание комментария, чтобы вы могли его увидеть.

Ответ №1:

Data<*> является общим супертипом Data<String> , Data<Any> , Data<AnythingYouPutThere> . Но Data<State<*>> не является общим супертипом Data<State<String>> etc.; это Data с определенным параметром типа State<*> (который является супертипом State<String> etc.)

К сожалению, Kotlin не поддерживает общие экзистенциальные типы, как это делает Scala, но в этих терминах Data<State<*>> Data<State<T> forSome T> , пока вы хотите Data<State<T>> forSome T .

Я установил mutableMapOf<String, Data<State<*>>>() mutableMapOf<String, Data<out State<*>>>() , и это сработало. Я не знаю, почему

Data<out State<*>> позволяет использовать любой подтип State<*> в качестве параметра типа Data . Таким образом, он также может быть выражен как экзистенциальный тип: Data<T> forSome T : State<*> , что не совсем Data<State<T>> forSome T так, потому State<*> что может иметь подтипы, которых нет State<Something> . Например, если у вас есть class State2 extends State<Int>() , Data<out State<*>> разрешает Data<State2> , но Data<State<T>> forSome T не будет.

Ответ №2:

Ознакомьтесь с документами здесь о проекции и дисперсии объявления

В нем говорится, что out требуется для указания компилятору типа <>

Ответ №3:

Согласно документам, допустим, у вас есть класс

 class Source<X>
  

вы не можете этого сделать

 val source: Source<Any> = Source<String>()
  

хотя String наследуется от Any. Это называется инвариантностью. Для поддержки этой функциональности вам придется использовать дисперсию сайта объявления Kotlin. Вам просто нужно добавить модификатор out к параметру типа X.

 class Source<out X>
  

Загвоздка в том, что вы не можете использовать X ни в одном из методов исходного класса, например

 class Source<out X> {
    fun modifyX(x: X) {} //not allowed
}
  

Вы можете создавать только X, т.е. Устанавливать возвращаемый тип методов на X