Nie jestem w stanie zrozumieć i nie mogłem znaleźć znaczenia słowa kluczowego out w kotlin.
Tutaj możesz sprawdzić przykład:
List<out T>
Jeśli ktoś może wyjaśnić znaczenie tego. Byłoby to naprawdę mile widziane.
Z tym podpisem:
List<out T>
możesz to zrobić:
val doubleList: List<Double> = listOf(1.0, 2.0)
val numberList: List<Number> = doubleList
co oznacza, że T jest kowariantne :
gdy parametr typu T klasy C deklaruje się , C <Podstawa> może być bezpiecznie supertypem od C <Pochodzące> .
Jest to kontrast z w , na przykład
Comparable<in T>
możesz to zrobić:
fun foo(numberComparable: Comparable<Number>) {
val doubleComparable: Comparable<Double> = numberComparable
// ...
}
co oznacza, że T jest sprzeczne :
gdy parametr typu T klasy C jest zadeklarowany w , C <Pochodzące> może być bezpiecznie supertypem od C <base> .
Inny sposób, aby to zapamiętać:
Konsument w , producent out .
zobacz Kotlin Generics Variance
----------------- aktualizacja 4 stycznia 2019 r. -----------------
W przypadku „ Consumer in, Producer out ”, czytamy tylko z metody Producer - call, aby uzyskać wynik typu T; i tylko pisz do Konsumenta - wywołaj metodę przekazując parametr typu T.
W przykładzie dla List<out T>
jest oczywiste, że możemy to zrobić:
val n1: Number = numberList[0]
val n2: Number = doubleList[0]
Więc jest to bezpieczne, List<Double>
kiedy List<Number>
się tego oczekuje, stąd List<Number>
jest super typ List<Double>
, ale nie odwrotnie.
W przykładzie dla Comparable<in T>
:
val double: Double = 1.0
doubleComparable.compareTo(double)
numberComparable.compareTo(double)
Więc jest to bezpieczne, Comparable<Number>
kiedy Comparable<Double>
się tego oczekuje, stąd Comparable<Double>
jest super typ Comparable<Number>
, ale nie odwrotnie.
List<out T>
deklaracji jest to, żeout
czyni ją niezmienną (w porównaniu ze zmiennymi kolekcjami, które nie mają wyjścia). Pomocne może być wspomnienie i podkreślenie tego w odpowiedzi. Rzutowanie niejawne jest konsekwencją tego, a nie głównego punktu (ponieważ nie można pisać do List <Number>, bezpiecznie jest mieć to jako odniesienie do List <Double>).out
to nie ta część sprawia, że jestList
niezmienny. Możesz łatwo stworzyć własnyList<out T>
interfejs, który maclear()
metodę, ponieważ nie wymagałby żadnych argumentów.List<out T> is like List<? extends T> in Java
i
List<in T> is like List<? super T> in Java
Na przykład w Kotlinie możesz robić takie rzeczy jak
val value : List<Any> = listOf(1,2,3) //since List signature is List<out T> in Kotlin
źródło
Zapoznaj się z instrukcją kotlin
I to,
źródło
Pamiętaj w ten sposób:
in
to „for in put” - chcesz coś do niego włożyć (napisać) (więc jest to „konsument”)out
jest „na wyciągnięcie ręki” - chcesz coś z tego wyciągnąć (przeczytać) (więc jest to „producent”)Jeśli jesteś z Javy,
<in T>
służy do wprowadzania danych, więc jest jak<? super T>
(konsument)<out T>
jest na wyjście, więc jest jak<? extends T>
(producent)źródło
Modyfikatory wariancji
out
iin
pozwalają nam uczynić nasze typy ogólne mniej restrykcyjnymi i łatwiejszymi do ponownego użycia, umożliwiając podtytuł.Zrozummy to za pomocą kontrastujących przykładów. Użyjemy przykładów skrzyń jako pojemników z różnymi rodzajami broni. Załóżmy, że mamy następującą hierarchię typów:
open class Weapon open class Rifle : Weapon() class SniperRifle : Rifle()
out
produkujeT
i zachowuje podtypyKiedy deklarujesz typ ogólny z
out
modyfikatorem, nazywa się to kowariantem . Kowariantna to producent zT
, co oznacza, że funkcje mogą wrócićT
, ale nie mogą braćT
jako argumenty: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
Zadeklarowana zout
modyfikator produkujeT
i jego podtypy:fun useProducer(case: Case<Rifle>) { // Produces Rifle and its subtypes val rifle = case.produce() }
W przypadku
out
modyfikatora podtyp jest zachowywany , więcCase<SniperRifle>
jest podtypemCase<Rifle>
kiedySniperRifle
jest podtypemRifle
. W rezultacieuseProducer()
funkcję można wywołaćCase<SniperRifle>
również za pomocą :useProducer(Case<SniperRifle>()) // OK useProducer(Case<Rifle>) // OK useProducer(Case<Weapon>()) // Error
Jest to mniej restrykcyjne i możliwe do ponownego wykorzystania podczas produkcji, ale nasza klasa staje się tylko do odczytu .
in
zużywaT
i odwraca podtypyKiedy deklarujesz typ ogólny z
in
modyfikatorem, jest on wywoływanycontravariant
. Kontrawariantny jest konsument zT
, co oznacza, że funkcje mogą wziąćT
jako argumenty, ale nie mogą wrócić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
Oświadczył zin
modyfikatorów zużywaT
i jego podtypy:fun useConsumer(case: Case<Rifle>) { // Consumes Rifle and its subtypes case.consume(SniperRifle()) }
Za pomocą
in
modyfikatora podtyp jest odwracany , więc terazCase<Weapon>
jest podtypemCase<Rifle>
kiedyRifle
jest podtypemWeapon
. W rezultacieuseConsumer()
funkcję można wywołaćCase<Weapon>
również za pomocą :useConsumer(Case<SniperRifle>()) // Error useConsumer(Case<Rifle>()) // OK useConsumer(Case<Weapon>()) // OK
Jest to mniej restrykcyjne i bardziej wielokrotnego użytku podczas konsumowania, ale nasza klasa staje się tylko do zapisu .
Invariant produkuje i konsumuje
T
, nie pozwala na podtypyKiedy deklarujesz typ ogólny bez żadnego modyfikatora wariancji, nazywa się to niezmiennym . Niezmiennik jest zarówno producentem, jak i konsumentem
T
, co oznacza, że funkcje mogą przyjmowaćT
jako argumenty, a także zwracać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
Zadeklarowana bezin
lubout
modyfikator produkuje i konsumujeT
i jego podtypy:fun useProducerConsumer(case: Case<Rifle>) { // Produces Rifle and its subtypes case.produce() // Consumes Rifle and its subtypes case.consume(SniperRifle()) }
Bez modyfikatora
in
or podtypout
jest niedozwolony , więc teraz aniCase<Weapon>
ani nieCase<SniperRifle>
jest podtypemCase<Rifle>
. W rezultacieuseProducerConsumer()
funkcję można wywołać tylko za pomocąCase<Rifle>
:useProducerConsumer(Case<SniperRifle>()) // Error useProducerConsumer(Case<Rifle>()) // OK useProducerConsumer(Case<Weapon>()) // Error
Jest to bardziej restrykcyjne i mniej wielokrotnego użytku podczas produkcji i konsumpcji, ale możemy czytać i pisać .
Wniosek
W
List
Kotlin jest tylko producentem. Ponieważ jest zadeklarowana za pomocąout
modyfikatora:List<out T>
. Oznacza to, że nie możesz dodawać do niego elementów, ponieważadd(element: T)
jest to funkcja konsumencka. Zawsze, gdy chcesz mieć możliwośćget()
równie dobrze jakadd()
elementy, użyj niezmiennej wersjiMutableList<T>
.Otóż to! Miejmy nadzieję, że to pomoże zrozumieć
in
s iout
s wariancji!źródło