Eine Fallklassen copy()
-Methode soll eine identische Kopie der Instanz erstellen und alle Felder nach Namen ersetzen. Dies scheint fehlzuschlagen, wenn die Fallklasse Typparameter mit Manifesten enthält. Die Kopie verliert alle Kenntnisse über die Arten ihrer Parameter.
Warum verliert Foo.copy
das Manifest in den Parametern und wie kann ich es beibehalten?
Mehrere Scala-Besonderheiten interagieren, um dieses Verhalten zu erzeugen. Die erste Sache ist, dass Manifest
s nicht nur an die geheime implizite Parameterliste im Konstruktor, sondern auch an die Kopiermethode angehängt wird. Es ist bekannt, dass
case class Foo[+A : Manifest](a: A)
ist nur syntaktischer Zucker für
case class Foo[+A](a: A)(implicit m: Manifest[A])
, aber das betrifft auch den Kopierkonstruktor, der so aussehen würde
def copy[B](a: B = a)(implicit m: Manifest[B]) = Foo[B](a)(m)
Alle diese implicit m
s werden vom Compiler erstellt und über die implizite Parameterliste an die Methode gesendet.
Das wäre in Ordnung, solange man die Methode copy
an einer Stelle verwendet, an der der Compiler den Parameter Foo
s type kennt. Dies funktioniert beispielsweise außerhalb der Bar-Klasse:
Das funktioniert, weil der Compiler folgert, dass foo
ein Foo[Int]
ist, also weiß es, dass foo.a
ein Int
ist, so dass es copy
wie folgt aufrufen kann:
val aCopy = foo.copy()(manifest[Int]())
(Beachten Sie, dass manifest[T]()
eine Funktion ist, die eine Manifest-Repräsentation vom Typ T
erzeugt, zB Manifest[T]
mit einem Großbuchstaben "M". Nicht gezeigt ist das Hinzufügen des Standardparameters in copy
.) Es funktioniert auch in der Klasse Foo
, da es bereits das Manifest enthält, das beim Erstellen der Klasse übergeben wurde. Es würde ungefähr so aussehen:
Im ursprünglichen Beispiel schlägt es jedoch in der Klasse Bar
aufgrund der zweiten Besonderheit fehl: Während die Typparameter von Bar
innerhalb der Klasse Bar
bekannt sind, sind die Typparameter der Typparameter nicht . Es weiß, dass A
in Bar
ein Foo
oder ein SubFoo
oder SubSubFoo
ist, aber nicht, wenn es sich um Foo[Int]
oder Foo[String]
handelt. Dies ist natürlich das bekannte Problem beim Löschen in Scala, aber es scheint hier ein Problem zu sein, auch wenn es nicht so aussieht, als würde die Klasse irgendetwas mit dem Typ% code% s type Parameter tun. Aber vergessen Sie nicht, dass jedes Mal, wenn foo
aufgerufen wird, eine Manifestation heimlich injiziert wird, und diese Manifeste überschreiben diejenigen, die vorher dort waren. Da die copy
-Klasse keine Ahnung hat, war der Typparameter von Bar
, er erstellt nur ein Manifest von foo
und sendet das wie folgt:
Any
Wenn man die def fooCopy = foo.copy()(manifest[Any])
-Klasse kontrolliert (zB ist es nicht Foo
), dann kann man es umgehen, indem man das Kopieren in der Foo-Klasse durchführt, indem man eine Methode hinzufügt, die das korrekte Kopieren ausführt, wie List
oben, und gebe das Ergebnis zurück:
Eine andere Lösung besteht darin, den Parameter localCopy
s type als manifest type-Parameter von Foo
hinzuzufügen:
Aber das skaliert schlecht, wenn die Klassenhierarchie groß ist (d. h. mehr Mitglieder haben Typparameter, und diese Klassen haben auch Typparameter), da jede Klasse die Typparameter jeder darunter liegenden Klasse haben müsste. Es scheint auch, dass die Typinferenz bei dem Versuch, ein Bar
zu konstruieren, ausflippt:
Es gibt zwei Probleme, die Sie identifiziert haben. Das erste Problem ist das Problem beim Löschen des Typs innerhalb von Bar
, wobei Bar
den Typ des Manifests von Foo
nicht kennt. Ich würde persönlich die localCopy
Workaround verwenden, die Sie vorgeschlagen haben.
Das zweite Problem besteht darin, dass ein weiterer impliziter Code heimlich in copy
eingefügt wird. Dieses Problem wird behoben, indem der Wert explizit erneut in copy
übergeben wird. Zum Beispiel:
Wir sehen, dass die Kopie das Int
manifest behalten hat, aber der Typ von fooCopy
und res2
ist Manifest[Any]
wegen Löschung.
Da ich Zugriff auf den impliziten Beweis benötigte, um copy
auszuführen, musste ich die explizite Syntax implicit
(hah) anstelle der kontextgebundenen Syntax verwenden. Aber die Verwendung der expliziten Syntax verursachte Fehler:
WTF? Wie funktioniert die kontextgebundene Syntax und das explizite implicit
nicht? Ich grub herum und fand eine Lösung für das Problem: die @uncheckedVariance
Annotation.
AKTUALISIEREN
Ich habe etwas mehr gegraben und festgestellt, dass in Scala 2.10 Fallklassen in nur geändert wurden. Kopieren Sie die Felder aus der ersten Parameterliste in copy()
.
Martin sagt: Fallklasse-Ness wird nur dem ersten Argument verliehen Liste den Rest sollte nicht kopiert werden.
Details zu dieser Änderung finden Sie unter Ссылка .
Tags und Links scala manifest type-erasure type-parameter