Es ist eine traurige Tatsache in Scala, dass Sie, wenn Sie eine Liste [Int] instanziieren, überprüfen können, ob Ihre Instanz eine Liste ist, und Sie können überprüfen, ob jedes einzelne Element davon eine Int ist, aber nicht, dass es eine Liste ist [ Int], wie leicht zu überprüfen ist:
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!
Mit der Option -unchecked liegt die Schuld direkt beim Löschen des Typs:
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
case l : List[String] => println("A list of strings?!")
^
A list of strings?!
Warum ist das so und wie komme ich darum herum?
scala
type-erasure
Daniel C. Sobral
quelle
quelle
TypeTag
s .scala 2.10.2
sah ich stattdessen diese Warnung:<console>:9: warning: fruitless type test: a value of type List[Int] cannot also be a List[String] (but still might match its erasure) case list: List[String] => println("a list of strings?") ^
Ich finde Ihre Frage und Antwort sehr hilfreich, bin mir aber nicht sicher, ob diese aktualisierte Warnung für die Leser nützlich ist.Antworten:
Scala wurde mit Type Erasure definiert, da die Java Virtual Machine (JVM) im Gegensatz zu Java keine Generika erhielt. Dies bedeutet, dass zur Laufzeit nur die Klasse vorhanden ist, nicht ihre Typparameter. In diesem Beispiel weiß JVM, dass es eine verarbeitet
scala.collection.immutable.List
, aber nicht, dass diese Liste mit parametrisiert istInt
.Glücklicherweise gibt es in Scala eine Funktion, mit der Sie das umgehen können. Es ist das Manifest . Ein Manifest ist eine Klasse, deren Instanzen Objekte sind, die Typen darstellen. Da es sich bei diesen Instanzen um Objekte handelt, können Sie sie weitergeben, speichern und im Allgemeinen Methoden für sie aufrufen. Mit der Unterstützung impliziter Parameter wird es zu einem sehr leistungsfähigen Werkzeug. Nehmen Sie zum Beispiel das folgende Beispiel:
Beim Speichern eines Elements speichern wir auch ein "Manifest" davon. Ein Manifest ist eine Klasse, deren Instanzen Scala-Typen darstellen. Diese Objekte enthalten mehr Informationen als JVM, sodass wir den vollständigen, parametrisierten Typ testen können.
Beachten Sie jedoch, dass sich a
Manifest
immer noch weiterentwickelt. Als Beispiel für seine Einschränkungen weiß es derzeit nichts über Varianz und geht davon aus, dass alles eine Co-Variante ist. Ich gehe davon aus, dass es stabiler und solider wird, sobald die derzeit in Entwicklung befindliche Scala-Reflexionsbibliothek fertig ist.quelle
get
Methode kann definiert werden alsfor ((om, v) <- _map get key if om <:< m) yield v.asInstanceOf[T]
.TypeTag
tatsächlich automatisch für den Mustervergleich verwendet werden? Cool, was?Manifest
Parameter selbst, siehe: stackoverflow.com/a/11495793/694469 "Die Instanz [manifest / type-tag] [...] wird implizit vom Compiler erstellt "Sie können dies mit TypeTags tun (wie Daniel bereits erwähnt, aber ich werde es nur explizit formulieren):
Sie können dies auch mit ClassTags tun (wodurch Sie sich nicht auf Scala-Reflect verlassen müssen):
ClassTags können verwendet werden, solange Sie nicht erwarten, dass der Typparameter
A
selbst ein generischer Typ ist.Leider ist es etwas ausführlich und Sie benötigen die Annotation @unchecked, um eine Compiler-Warnung zu unterdrücken. Das TypeTag wird möglicherweise in Zukunft vom Compiler automatisch in die Musterübereinstimmung integriert: https://issues.scala-lang.org/browse/SI-6517
quelle
[List String @unchecked]
da es dieser Musterübereinstimmung nichts hinzufügt (nur die Verwendung reichtcase strlist if typeOf[A] =:= typeOf[String] =>
aus, oder selbstcase _ if typeOf[A] =:= typeOf[String] =>
wenn die gebundene Variable im Hauptteil von nicht benötigt wirdcase
).=>
ausgeführt wird. (Und wenn der Code auf dem rhs ausgeführt wird, geben die Wachen eine statische Garantie für die Art der Elemente. Es kann eine Besetzung geben, aber es ist sicher.)Du kannst den ... benutzen
Typeable
Typklasse von formlos verwenden , um das gewünschte Ergebnis zu erhalten.Beispiel einer REPL-Sitzung,
Der
cast
Vorgang wird angesichts derTypeable
verfügbaren In-Scope- Instanzen so genau wie möglich gelöscht .quelle
l1.cast[List[String]]
ungefährfor (x<-l1) assert(x.isInstanceOf[String]
) Bei großen Datenstrukturen oder wenn die Casts sehr häufig auftreten, kann dies ein inakzeptabler Overhead sein.Ich habe eine relativ einfache Lösung gefunden, die in Situationen mit eingeschränkter Verwendung ausreicht und im Wesentlichen parametrisierte Typen umschließt, die unter dem Typlöschproblem in Wrapper-Klassen leiden, die in einer Übereinstimmungsanweisung verwendet werden können.
Dies hat die erwartete Ausgabe und begrenzt den Inhalt unserer Fallklasse auf den gewünschten Typ, String Lists.
Weitere Details hier: http://www.scalafied.com/?p=60
quelle
Es gibt eine Möglichkeit, das Problem der Typlöschung in Scala zu lösen. Unter Überwinden der Typlöschung in Matching 1 und Überwinden der Typlöschung in Matching 2 (Varianz) finden Sie einige Erläuterungen zum Codieren einiger Helfer, um die Typen, einschließlich Varianz, für den Matching zu verpacken.
quelle
Ich habe eine etwas bessere Problemumgehung für diese Einschränkung der ansonsten fantastischen Sprache gefunden.
In Scala tritt das Problem der Typlöschung bei Arrays nicht auf. Ich denke, es ist einfacher, dies anhand eines Beispiels zu demonstrieren.
Nehmen wir an, wir haben eine Liste von
(Int, String)
, dann gibt das Folgende eine Art LöschwarnungUm dies zu umgehen, erstellen Sie zunächst eine Fallklasse:
dann mache im Pattern Matching so etwas wie:
das scheint perfekt zu funktionieren.
Dies erfordert geringfügige Änderungen in Ihrem Code, um mit Arrays anstelle von Listen zu arbeiten, sollte jedoch kein großes Problem darstellen.
Beachten Sie, dass bei Verwendung
case a:Array[(Int, String)]
weiterhin eine Warnung zum Löschen des Typs ausgegeben wird. Daher muss eine neue Containerklasse verwendet werden (in diesem BeispielIntString
).quelle
Da Java den tatsächlichen Elementtyp nicht kennt, fand ich es am nützlichsten, ihn nur zu verwenden
List[_]
. Dann verschwindet die Warnung und der Code beschreibt die Realität - es ist eine Liste von etwas Unbekanntem.quelle
Ich frage mich, ob dies eine geeignete Problemumgehung ist:
Es stimmt nicht mit dem Fall "leere Liste" überein, aber es gibt einen Kompilierungsfehler, keine Warnung!
Dies scheint auf der anderen Seite zu funktionieren ....
Ist es nicht noch besser oder verpasse ich den Punkt hier?
quelle
Keine Lösung, sondern eine Möglichkeit, damit zu leben, ohne es unter den Teppich zu kehren: Hinzufügen der
@unchecked
Anmerkung. Siehe hier - http://www.scala-lang.org/api/current/index.html#scala.uncheckedquelle
Ich wollte eine Antwort hinzufügen, die das Problem verallgemeinert: Wie erhalte ich zur Laufzeit eine String-Darstellung des Typs meiner Liste?
quelle
Verwenden des Pattern Match Guard
quelle
isInstanceOf
eine Laufzeitprüfung basierend auf den Typinformationen durchgeführt wird, die der JVM zur Verfügung stehen. Und diese Laufzeitinformationen enthalten nicht das Argument type toList
(aufgrund der Typlöschung).