Я не могу понять и не могу найти значение ключевого слова 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, outV>/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с и outS дисперсии!
List<out T>объявления является то, чтоoutоно делает его неизменным (по сравнению с изменяемыми коллекциями, у которых нет out). Возможно, стоит упомянуть и подчеркнуть это в ответе. Неявное приведение типов является следствием этого, а не основным моментом (поскольку нельзя писать в List <Number>, безопасно иметь его как ссылку на List <Double>).