Ich untersuche den Quellcode der Scala 2.8 Collection Klassen. Ich habe Fragen zur Hierarchie von scala.collection.Traversable
. Sehen Sie sich die folgenden Erklärungen an:
Frage: Warum erweitert Traversable
GenericTraversableTemplate
mit den Typparametern [A, Traversable]
- warum nicht [A, Traversable[A]]
? Ich habe versucht, mit einem kleinen Programm mit der gleichen Struktur zu experimentieren und habe eine seltsame Fehlermeldung erhalten, als ich versucht habe, es in Traversable[A]
zu ändern:
Ich nehme an, dass die Verwendung der @uncheckedVariance
Annotation in GenericTraversableTemplate
auch damit zu tun hat? (Das scheint eine Art potentiell unsicherer Hack zu sein, um Dinge zur Arbeit zu zwingen ...).
edit - einige nützliche Antworten auf die Annotation in diese Frage (weil GenericTraversableTemplate
sowohl für veränderbare als auch für unveränderbare Sammlungen verwendet wird, die unterschiedliche Varianz haben).
Frage: Wenn Sie sich die Hierarchie ansehen, sehen Sie, dass Traversable
zweimal HasNewBuilder
erbt (einmal über TraversableLike
und einmal über GenericTraversableTemplate
), aber mit etwas anderen Typparametern. Wie funktioniert das genau? Warum verursachen die verschiedenen Typparameter keinen Fehler?
Der Grund ist der CC
Parameter im GenericTraversableTemplate
Merkmal. Im Gegensatz zu einem normalen Typparameter, der die Art *
hat (ausgesprochen "Typ"), hat dieser Parameter den Typ * => *
(ausgesprochen "type to type"). Um zu verstehen, was das bedeutet, müssen Sie zuerst ein wenig Hintergrundwissen über Arten haben.
Betrachten Sie das folgende Snippet:
%Vor% Hier sehen wir 42
, was ein Wert ist. Werte haben intrinsische Typen. In diesem Fall ist unser Wert 42
und der Typ Int
. Ein Typ ist so etwas wie eine Kategorie, die viele Werte umfasst. Es sagt etwas über die Werte aus, die für die Variable a
möglich sind. Zum Beispiel wissen wir, dass a
den Wert "foobar"
nicht enthalten kann, da dieser Wert den Typ String
hat. Daher sind die Werte eine Art der ersten Abstraktionsebene, während die Typen eine Ebene über den Werten liegen.
Hier ist also die Frage: Was hält uns davon ab, diesen einen Schritt weiter zu gehen? Wenn Werte Typen haben können, warum können Typen nicht "etwas" über ihnen haben? Dieses "Etwas" wird als Art bezeichnet. Typen geben ein, welche Typen zu Werten gehören, generische Kategorien, die einschränken, welche Art von Typen beschrieben werden kann.
Sehen wir uns einige konkrete Beispiele an:
%Vor% Dies sind Typen, und alle haben eine Art *
. Dies ist die gebräuchlichste Art (weshalb wir es "Typ" nennen). In der Praxis haben die meisten Typen diese Art. Einige jedoch nicht:
Hier haben wir den Typkonstruktor List
, aber dieses Mal haben wir "vergessen", seinen Typparameter anzugeben. Wie sich herausstellt, handelt es sich tatsächlich um einen Typ, der jedoch von anderer Art ist. Insbesondere * => *
. Wie die Notation andeuten soll, beschreibt diese Art einen Typ, der einen anderen Typ der Art *
als Parameter annimmt, wodurch ein neuer Typ von Art *
erzeugt wird. Wir können dies im ersten Beispiel sehen, wo wir den Typ Int
(welcher *
hat) an den Konstruktor List
übergeben haben (welcher * => *
hat), was den Typ List[Int]
(was kind *
).
Kehren wir zu GenericTraversableTemplate
zurück, schauen wir uns noch einmal die Deklaration an:
Beachten Sie, dass der Parameter CC
type einen eigenen Parameter verwendet, dieser Parameter jedoch von keinem anderen Typparameter in der Deklaration definiert ist? Das ist Scalas ziemlich ungeschickte Art zu sagen, dass CC
vom Typ * => *
sein muss (genauso wie a
in unserem früheren Beispiel vom Typ Int
sein muss). "Normal" -Typ-Parameter (zB A
) sind immer vom Typ *
. Durch das Erzwingen von CC
zur Art * => *
teilen wir dem Compiler effektiv mit, dass die einzigen gültigen Typen, die diesen Parameter ersetzen können, selbst vom Typ * => *
sein müssen. Also:
Denken Sie daran, List
ist vom Typ * => *
(genau das, was wir für CC
brauchen), aber List[Int]
hat kind *
, so dass der Compiler es ablehnt.
Für den Datensatz hat GenericTraversableTemplate
selbst eine Art, insbesondere: (* x (* => *)) => *
. Dies bedeutet, dass GenericTraversableTemplate
ein Typ ist, der zwei Typen als Parameter akzeptiert - einen vom Typ *
, den anderen vom Typ * => *
- und als Ergebnis einen Typ vom Typ *
erzeugt. In unserem obigen Beispiel ist GenericTraversableTemplate[String, List]
ein solcher Ergebnistyp, und wie wir berechnet haben, ist er vom Typ *
(er benötigt keine Parameter).
Tags und Links scala generics scala-2.8 scala-collections