Я не могу понять и не могу найти значение ключевого слова 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>
, но не наоборот.
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
Тип Kotlin
List<out T>
- это интерфейс, который предоставляет операции только для чтения, такие как size, get и т. Д. Как и в Java, он наследуется от,Collection<T>
а тот, в свою очередь, наследуется отIterable<T>
. Методы, изменяющие список, добавляютсяMutableList<T>
интерфейсом. Этот образец справедлив также дляSet<out T>/MutableSet<T>
иMap<K, out
V>/MutableMap<K, V>
И это,
В Kotlin есть способ объяснить компилятору подобные вещи. Это называется отклонением на объекте объявления: мы можем аннотировать параметр типа T объекта Source, чтобы убедиться, что он только возвращается (создается) из членов
Source<T>
и никогда не используется. Для этого мы предоставляем модификатор out:
> abstract class Source<out T> { > abstract fun nextT(): T } > > fun demo(strs: Source<String>) { > val objects: Source<Any> = strs // This is OK, since T is an out-parameter > // ... }
Общее правило: когда параметр
T
типа классаC
объявляется вне, он может встречаться только вне позиции в членах классаC
, но взаменC<Base>
может безопасно быть супертипом классаC<Derived>
.В «умных словах» они говорят, что класс
C
ковариантен по параметруT
, или чтоT
это параметр ковариантного типа. Вы можете думать о С как о производителе Т, а НЕ как о потребителеT
. Модификатор out называется аннотацией отклонения, и, поскольку он предоставляется на сайте объявления параметра типа, мы говорим об отклонении на сайте объявления. Это контрастирует с вариативностью сайта использования Java, где подстановочные знаки в использовании типов делают типы ковариантными.
Запомните вот так:
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 дисперсии!
List<out T>
объявления является то, чтоout
оно делает его неизменным (по сравнению с изменяемыми коллекциями, у которых нет out). Возможно, стоит упомянуть и подчеркнуть это в ответе. Неявное приведение типов является следствием этого, а не основным моментом (поскольку нельзя писать в List <Number>, безопасно иметь его как ссылку на List <Double>).