Was ist aus Schlüsselwort in Kotlin

80

Ich bin nicht in der Lage zu verstehen , und ich konnte nicht die Bedeutung finde heraus Schlüsselwort in Kotlin.

Sie können das Beispiel hier überprüfen:

List<out T>

Wenn jemand die Bedeutung davon erklären kann. Es wäre sehr dankbar.

Akshay Sood
quelle

Antworten:

57

Mit dieser Unterschrift:

List<out T>

du kannst das:

val doubleList: List<Double> = listOf(1.0, 2.0)
val numberList: List<Number> = doubleList

was bedeutet , T ist kovarianten :

Wenn ein Typparameter T einer Klasse C deklariert wird , kann C <Basis> sicher ein Supertyp von C <Derived> sein .

Dies ist Kontrast mit in , zB

Comparable<in T>

du kannst das:

fun foo(numberComparable: Comparable<Number>) {
  val doubleComparable: Comparable<Double> = numberComparable
  // ...
}

was bedeutet , T ist kontra :

wenn ein Typparameter T einer Klasse C wird erklärt in , C <Derived> kann sicher sein , übergeordnete Typ von C <Base> .

Eine andere Art, sich daran zu erinnern:

Verbraucher in , Produzent aus .

siehe Kotlin Generics Varianz

----------------- aktualisiert am 4. Januar 2019 -----------------

Für " Consumer in, Producer out " lesen wir nur von der Producer-Call-Methode, um das Ergebnis vom Typ T zu erhalten. und schreiben Sie nur in die Consumer-Call-Methode, indem Sie den Parameter vom Typ T übergeben.

Im Beispiel für List<out T>ist es offensichtlich, dass wir dies tun können:

val n1: Number = numberList[0]
val n2: Number = doubleList[0]

So ist es sicher zu liefern, List<Double>wann List<Number>erwartet wird, daher List<Number>ist super Typ List<Double>, aber nicht umgekehrt.

Im Beispiel für Comparable<in T>:

val double: Double = 1.0
doubleComparable.compareTo(double)
numberComparable.compareTo(double)

So ist es sicher zu liefern, Comparable<Number>wann Comparable<Double>erwartet wird, daher Comparable<Double>ist super Typ Comparable<Number>, aber nicht umgekehrt.

Andrew Feng
quelle
1
Ich denke, der wichtigste Punkt für eine sehende List<out T>Erklärung ist, outdass sie unveränderlich ist (im Vergleich zu veränderlichen Sammlungen, die keine haben). Es kann hilfreich sein, dies in der Antwort zu erwähnen und hervorzuheben. Implizites Casting ist eher eine Folge dieses als eines Hauptpunkts (da man nicht in Liste <Nummer> schreiben kann, kann man es sicher als Referenz auf Liste <Doppel> haben).
Minsk
34
Entschuldigung, konnte es aber immer noch nicht verstehen.
Akshay Taru
2
@minsk Der outTeil ist jedoch nicht das, was Listunveränderlich macht . Sie können ganz einfach eine eigene List<out T>Schnittstelle erstellen, die über eine clear()Methode verfügt , da keine Argumente erforderlich sind.
Nick Lowery
Dies ist eines der Themen, die Sie wahrscheinlich erhalten möchten, bis Sie es wirklich irgendwo in Ihrem Code benötigen.
lasec0203
98
List<out T> is like List<? extends T> in Java

und

List<in T> is like List<? super T> in Java

Zum Beispiel können Sie in Kotlin Dinge wie tun

 val value : List<Any> = listOf(1,2,3)
//since List signature is List<out T> in Kotlin
DmitryBorodin
quelle
1
Diese Antwort ist rückwärts. List<out T>bedeutet, dass Sie tun können, val list: List<Number> = listOf<Int>()weil Intes sich um einen abgeleiteten Typ von handelt Number. Das entsprechende Java wäreList<? extends Number> list = new ArrayList<Integer>();
Nick Lowery
Guter Punkt, in Java ist es "? Erweitert T", nicht "? Super T". Fest. Ich habe nicht erwähnt, dass wir Varianz an verschiedenen Stellen definieren, um die Antwort einfach zu halten. Jeder kann zur offiziellen Dokumentation gehen, um alle Details zu erfahren. Also habe ich nicht verstanden, was du mit "Antwort ist rückwärts" gemeint hast.
DmitryBorodin
4

Siehe das Handbuch von Kotlin

Der Kotlin- List<out T>Typ ist eine Schnittstelle, die schreibgeschützte Operationen wie size, get usw. bereitstellt. Wie in Java erbt es von Collection<T>und das wiederum erbt von Iterable<T>. Methoden, die die Liste ändern, werden von der MutableList<T>Schnittstelle hinzugefügt . Dieses Muster gilt auch für Set<out T>/MutableSet<T>undMap<K, out V>/MutableMap<K, V>

Und das,

In Kotlin gibt es eine Möglichkeit, dem Compiler so etwas zu erklären. Dies wird als Varianz der Deklarationssite bezeichnet: Wir können den Typparameter T von Source mit Anmerkungen versehen, um sicherzustellen, dass er nur von Mitgliedern von zurückgegeben (produziert) Source<T>und niemals verbraucht wird. Dazu stellen wir den Modifikator out zur Verfügung:

> 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
>     // ... }

Die allgemeine Regel lautet: Wenn ein Typparameter Teiner Klasse Cals deklariert wird, kann er nur in der Out-Position in den Mitgliedern von auftreten C, kann aber im Gegenzug C<Base>sicher ein Supertyp von sein C<Derived>.

In "klugen Worten" sagen sie, dass die Klasse Cim Parameter kovariant Tist oder dass Tes sich um einen kovarianten Typparameter handelt. Sie können sich C als Produzenten von T's und NICHT als Konsument von T's vorstellen. Der Out-Modifikator wird als Varianzanmerkung bezeichnet. Da er an der Deklarationsstelle für Typparameter bereitgestellt wird, wird von der Varianz der Deklarationsstelle gesprochen. Dies steht im Gegensatz zu Javas Varianz der Verwendungsorte, bei der Platzhalter in den Typverwendungen die Typen kovariant machen.

Kris Roofe
quelle