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.
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.
Odpowiedzi:
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.
outto nie ta część sprawia, że jest Listniezmienny. Możesz łatwo stworzyć własny List<out T>interfejs, który ma clear()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
Zapoznaj się z instrukcją kotlin
Typ Kotlin
List<out T>to interfejs, który zapewnia operacje tylko do odczytu, takie jak size, get i tak dalej. Podobnie jak w Javie, dziedziczy on z,Collection<T>a to z kolei dziedziczy zIterable<T>.MutableList<T>Interfejs dodaje metody zmieniające listę . Ten wzór dotyczy równieżSet<out T>/MutableSet<T>iMap<K, outV>/MutableMap<K, V>
I to,
W Kotlinie istnieje sposób na wyjaśnienie tego typu rzeczy kompilatorowi. Nazywa się to wariancją witryny deklaracji: możemy dodać adnotację do parametru typu T źródła, aby upewnić się, że jest on zwracany (produkowany) tylko od elementów członkowskich
Source<T>i nigdy nie jest używany. Aby to zrobić, podajemy modyfikator 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 > // ... }Ogólna zasada jest taka: kiedy parametr typu
TklasyCjest zadeklarowany jako out, może wystąpić tylko w pozycji wyjściowej w elementach członkowskichC, ale w zamianC<Base>może bezpiecznie być nadtypemC<Derived>.W „sprytnych słowach” mówią, że klasa
Cjest kowariantna w parametrzeTlub żeTjest to parametr typu kowariantnego. Możesz myśleć o C jako o producencie T, a NIE o konsumentachT. Modyfikator out jest nazywany adnotacją wariancji, a ponieważ jest dostarczany w miejscu deklaracji parametru typu, mówimy o wariancji witryny deklaracji. Kontrastuje to z wariancją miejsca użycia w Javie, gdzie symbole wieloznaczne w zastosowaniach typów powodują, że typy są kowariantne.
Pamiętaj w ten sposób:
into „for in put” - chcesz coś do niego włożyć (napisać) (więc jest to „konsument”)
outjest „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)
Modyfikatory wariancji outi inpozwalają 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()
outprodukuje Ti zachowuje podtypyKiedy deklarujesz typ ogólny z outmodyfikatorem, nazywa się to kowariantem . Kowariantna to producent z T, co oznacza, że funkcje mogą wrócić T, ale nie mogą brać Tjako 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
}
CaseZadeklarowana z outmodyfikator produkuje Ti jego podtypy:
fun useProducer(case: Case<Rifle>) {
// Produces Rifle and its subtypes
val rifle = case.produce()
}
W przypadku outmodyfikatora podtyp jest zachowywany , więc Case<SniperRifle>jest podtypem Case<Rifle>kiedy SniperRiflejest podtypem Rifle. W rezultacie useProducer()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 .
inzużywa Ti odwraca podtypyKiedy deklarujesz typ ogólny z inmodyfikatorem, jest on wywoływany contravariant. Kontrawariantny jest konsument z T, co oznacza, że funkcje mogą wziąć Tjako 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
}
CaseOświadczył z inmodyfikatorów zużywa Ti jego podtypy:
fun useConsumer(case: Case<Rifle>) {
// Consumes Rifle and its subtypes
case.consume(SniperRifle())
}
Za pomocą inmodyfikatora podtyp jest odwracany , więc teraz Case<Weapon>jest podtypem Case<Rifle>kiedy Riflejest podtypem Weapon. W rezultacie useConsumer()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 .
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ć Tjako 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
}
CaseZadeklarowana bez inlub outmodyfikator produkuje i konsumuje Ti jego podtypy:
fun useProducerConsumer(case: Case<Rifle>) {
// Produces Rifle and its subtypes
case.produce()
// Consumes Rifle and its subtypes
case.consume(SniperRifle())
}
Bez modyfikatora inor podtyp outjest niedozwolony , więc teraz ani Case<Weapon>ani nie Case<SniperRifle>jest podtypem Case<Rifle>. W rezultacie useProducerConsumer()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ć .
W ListKotlin jest tylko producentem. Ponieważ jest zadeklarowana za pomocą outmodyfikatora: 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 jak add()elementy, użyj niezmiennej wersji MutableList<T>.
Otóż to! Miejmy nadzieję, że to pomoże zrozumieć ins i outs wariancji!
List<out T>deklaracji jest to, żeoutczyni 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>).