Я не могу понять и не могу найти значение ключевого слова out в котлине.
Вы можете проверить пример здесь:
List<out T>
Если кто-нибудь может объяснить значение этого. Было бы очень признательно.
С этой подписью:
List<out T>
ты можешь это сделать:
val doubleList: List<Double> = listOf(1.0, 2.0)
val numberList: List<Number> = doubleList
что означает Т является ковариантным :
когда тип параметра T из класса C объявляется вне , С <База> может безопасно быть супертипом из C <Derived> .
Это контрастирует с в , например ,
Comparable<in T>
ты можешь это сделать:
fun foo(numberComparable: Comparable<Number>) {
val doubleComparable: Comparable<Double> = numberComparable
// ...
}
что означает Т является контравариантным :
когда тип параметра T из класса C объявляется в , C <Derived> может безопасно быть супертипом из C <Base> .
Другой способ запомнить:
Потребитель в , продюсер отъезда .
----------------- обновлено 4 января 2019 г. -----------------
Для « Потребитель входит, производитель выходит » мы читаем только из источника - вызываем метод, чтобы получить результат типа T; и писать только в метод Consumer - call, передав параметр типа T.
В примере для List<out T>
очевидно, что мы можем сделать это:
val n1: Number = numberList[0]
val n2: Number = doubleList[0]
Таким образом, безопасно предоставлять, List<Double>
когда List<Number>
ожидается, следовательно, List<Number>
это супер тип List<Double>
, но не наоборот.
В примере для Comparable<in T>
:
val double: Double = 1.0
doubleComparable.compareTo(double)
numberComparable.compareTo(double)
Таким образом, безопасно предоставлять, Comparable<Number>
когда Comparable<Double>
ожидается, следовательно, Comparable<Double>
это супер тип Comparable<Number>
, но не наоборот.
List<out T>
объявления является то, чтоout
оно делает его неизменным (по сравнению с изменяемыми коллекциями, у которых нет out). Возможно, стоит упомянуть и подчеркнуть это в ответе. Неявное приведение типов является следствием этого, а не основным моментом (поскольку нельзя писать в List <Number>, безопасно иметь его как ссылку на List <Double>).out
не в том, что делаетList
неизменяемым. Вы можете легко создать свой собственныйList<out T>
интерфейс, в котором естьclear()
метод, поскольку он не требует аргументов.List<out T> is like List<? extends T> in Java
и
List<in T> is like List<? super T> in Java
Например, в Котлине вы можете делать такие вещи, как
val value : List<Any> = listOf(1,2,3) //since List signature is List<out T> in Kotlin
источник
Обратитесь к руководству kotlin
И это,
источник
Запомните вот так:
in
"for in put" - вы хотите поместить (написать) что-то в него (так что это "потребитель")out
является «для из пут» - вы хотите взять (прочитать) что - то из него (так что это «продюсер»)Если вы с Java,
<in T>
предназначен для ввода, поэтому это похоже на<? super T>
(потребитель)<out T>
предназначен для вывода, так что это похоже на<? extends T>
(продюсер)источник
Модификаторы дисперсии
out
иin
позволяют нам сделать наши универсальные типы менее ограничительными и более пригодными для повторного использования, разрешив создание подтипов.Давайте разберемся в этом с помощью контрастных примеров. Будем использовать примеры ящиков как контейнеров для различного оружия. Предположим, что у нас есть следующая иерархия типов:
open class Weapon open class Rifle : Weapon() class SniperRifle : Rifle()
out
производитT
и сохраняет подтипыКогда вы объявляете универсальный тип с
out
модификатором, он называется ковариантным . Ковариантный является производителем изT
, это означает , что функции могут возвращать ,T
но они не могут принимать вT
качестве аргументов:class Case<out T> { private val contents = mutableListOf<T>() fun produce(): T = contents.last() // Producer: OK fun consume(item: T) = contents.add(item) // Consumer: Error }
Case
Объявлен сout
модификатором производитT
и его подтипы:fun useProducer(case: Case<Rifle>) { // Produces Rifle and its subtypes val rifle = case.produce() }
С
out
модификатором подтип сохраняется , поэтомуCase<SniperRifle>
is подтип,Case<Rifle>
когдаSniperRifle
является подтипомRifle
. В результатеuseProducer()
функция также может быть вызванаCase<SniperRifle>
:useProducer(Case<SniperRifle>()) // OK useProducer(Case<Rifle>) // OK useProducer(Case<Weapon>()) // Error
Это менее ограничительно и более пригодно для повторного использования при производстве, но наш класс становится доступным только для чтения .
in
потребляетT
и меняет подтипированиеКогда вы объявляете универсальный тип с
in
модификатором, он вызываетсяcontravariant
. Контравариантный является потребителем изT
, это означает , что функции могут принимать вT
качестве аргументов , но они не могут вернутьсяT
:class Case<in T> { private val contents = mutableListOf<T>() fun produce(): T = contents.last() // Producer: Error fun consume(item: T) = contents.add(item) // Consumer: OK }
Case
Объявлен сin
модификаторами потребляетT
и его подтипов:fun useConsumer(case: Case<Rifle>) { // Consumes Rifle and its subtypes case.consume(SniperRifle()) }
С помощью
in
модификатора подтипы меняются местами , поэтому теперьCase<Weapon>
подтип это подтип,Case<Rifle>
когдаRifle
является подтипомWeapon
. В результатеuseConsumer()
функция также может быть вызванаCase<Weapon>
:useConsumer(Case<SniperRifle>()) // Error useConsumer(Case<Rifle>()) // OK useConsumer(Case<Weapon>()) // OK
Это менее ограничительно и более многоразово при использовании, но наш класс становится только для записи .
Инвариант производит и потребляет
T
, запрещает выделение подтиповКогда вы объявляете универсальный тип без модификатора дисперсии, он называется инвариантом . Инвариант является производителем и потребителем
T
, что означает, что функции могут приниматьT
как аргументы, а также возвращатьT
:class Case<T> { private val contents = mutableListOf<T>() fun produce(): T = contents.last() // Producer: OK fun consume(item: T) = contents.add(item) // Consumer: OK }
В
Case
Объявлено безin
илиout
модификатора производит и потребляетT
и его подтипы:fun useProducerConsumer(case: Case<Rifle>) { // Produces Rifle and its subtypes case.produce() // Consumes Rifle and its subtypes case.consume(SniperRifle()) }
Без модификатора
in
илиout
выделение подтипов запрещено , поэтому сейчасCase<Weapon>
ниCase<SniperRifle>
подтип один из подтипов являетсяCase<Rifle>
. В результатеuseProducerConsumer()
функция может быть вызвана только сCase<Rifle>
:useProducerConsumer(Case<SniperRifle>()) // Error useProducerConsumer(Case<Rifle>()) // OK useProducerConsumer(Case<Weapon>()) // Error
Это более ограничительно и менее пригодно для повторного использования при производстве и потреблении, но мы можем читать и писать .
Заключение
В
List
Котлине только производитель. Потому что он объявлен с использованиемout
модификатора:List<out T>
. Это означает, что вы не можете добавлять к нему элементы, посколькуadd(element: T)
это функция потребителя. Всякий раз, когда вы хотите иметь возможность неget()
хужеadd()
элементов, используйте инвариантную версиюMutableList<T>
.Это оно! Надеюсь , что помогает понять
in
с иout
S дисперсии!источник