#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