Für den folgenden Code:
%Vor% val a
wird ohne Ausnahme berechnet und die Konsole druckt 1: two
,
aber wenn val b
berechnet wird, wird java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
geworfen.
Außerdem, wenn ich
ausführe %Vor%Die Konsole druckt "int".
Kann jemand erklären, warum sich dieser Code so verhält?
Das ist sicher merkwürdig.
Wenn Sie sich die folgende Anweisung in der Scala REPL ansehen:
%Vor% Was ich denke, ist folgendes: Die Überprüfen wir dies Schritt für Schritt in der REPL : So weit, so gut. Beachten Sie, dass bisher keine Ausnahmen ausgelöst wurden, obwohl wir Nun führen wir die Zuweisung zu einer Jetzt bekommen wir die Ausnahme! Beachten Sie auch die Funktion in der Stack-Ablaufverfolgung, die sie verursacht hat: Ein großer Teil des Problems ist type löschen . Vergessen Sie nicht, dass ein Schließlich in Bezug auf die Um zu überprüfen, ob das Objekt tatsächlich ein Es folgt eine weitere Überprüfung des Rückgabewerts von asInstanceOf[T]
Anweisung sagt dem Compiler einfach, dass das Ergebnis ein Int
sein soll, aber keine Umwandlung ist erforderlich, weil das Objekt nur noch über einen Zeiger referenziert wird. (Und Int
-Werte sind innerhalb eines Option
/ Some
eingerahmt) .toString
funktioniert, weil jedes Objekt eine .toString
-Methode hat, die nur auf den Wert "two" wirkt, um "two" zu erhalten. Wenn Sie jedoch versuchen, das Ergebnis einer Int
-Variable zuzuweisen, versucht der Compiler, die gespeicherte Ganzzahl aufzuheben, und das Ergebnis ist eine Cast-Ausnahme, da der Wert ein String
und nicht ein eingerahmtes Int
. .asInstanceOf[T]
bereits verwendet und get
für den resultierenden Wert verwendet haben. Was bedeutsam ist, ist, dass wir nichts mit dem Ergebnis des Aufrufs get
versucht haben (nominell ein eingerahmtes Int
, das ist tatsächlich der String
-Wert "two"), außer dass es seine Methode toString
aufruft. Das funktioniert, weil String
-Werte toString
Methoden haben. Int
-Variable durch: unboxToInt
- es versucht eindeutig, den in Some
gespeicherten Wert in ein Int
zu konvertieren, und es schlägt fehl, weil es kein eingerahmtes Int
sondern ein String.
ist Some(Banana)
und ein Some(Bicycle)
- zur Laufzeit - nur Some
Instanzen mit einem Zeiger auf ein Objekt sind. .asInstanceOf[T]
kann den Typ nicht überprüfen, da diese Information gelöscht wurde. Der Compiler ist jedoch in der Lage zu verfolgen, welcher Typ auf dem basiert, was Sie ihm mitgeteilt haben, aber er kann den Fehler nur erkennen, wenn seine Annahmen falsch sind. getClass
Aufruf auf das Ergebnis. Dies ist ein bisschen Compilerschleudern. Es ruft nicht tatsächlich eine getClass
-Funktion für das Objekt auf, sondern - da es denkt, es handelt sich um ein Int
, das ein Primitiv ist - ersetzt es einfach eine int
-Klasseninstanz. String
ist, können Sie es wie folgt in eine Any
umwandeln: get
; Beachten Sie das Fehlen einer Ausnahme, wenn wir das Ergebnis dieser Methode einer Variablen vom Typ Any
zuordnen (also kein Casting notwendig ist), die Tatsache, dass das gültige Int
mit Schlüssel "1" tatsächlich unter Any
gespeichert ist als Boxed Int
( java.lang.Integer
), und dieser letztere Wert kann erfolgreich auf ein normales Int
Primitiv entpackt werden:
Erweitern Sie den letzten Teil der Antwort von eje211 .
Sie haben dem Compiler gesagt, dass ein String
ein Int
ist, und jetzt suchen Sie nach einer sinnvollen Erklärung für das resultierende Verhalten. Das ist verständlich, aber es ist nicht wirklich nützlich. Sobald Sie dem Compiler eine Lüge erzählen, sind alle Wetten deaktiviert. Sie können Zeit damit verbringen, genau zu untersuchen, wann und wo der Compiler Prüfungen einfügt, die Ihren Betrug entdecken, aber Ihre Zeit wäre wahrscheinlich besser, Code zu schreiben, der Sie nicht zum Lügen bringt.
Wie die frühere Antwort gezeigt hat, können Sie das tun (vermeiden Sie versehentliches Lügen), indem Sie den Mustervergleich verwenden. Sie benötigen ein ClassTag
, um Mustervergleiche in Fällen wie dem oben genannten auszuführen, aber das Endergebnis ist Code, der typsicher und korrekt ist.
Es ist ein Int
, weil Sie in dieser Zeile Int
anfordern:
Dies ruft diese Methode auf:
%Vor%und wendet es so an:
%Vor% Wenn Sie also nach einem Int
fragen, erhalten Sie Int
.
Im Falle von "two"
geschieht das in diesem Fall:
Das ist, was Ihre Ausnahme auslöst.
A String
ist kein Int. Du kannst es nicht so darstellen. Sie können es diesen Weg werfen:
Aber das ist anders.
Im Allgemeinen ist die Verwendung von asInstanceOf[]
gefährlich. Versuchen Sie stattdessen die Mustererkennung. Wenn Sie müssen verwenden, müssen Sie sicherstellen, dass die von Ihnen versuchten Umwandlungen gültig sind. Sie weisen den Compiler grundsätzlich an, seine eigenen Typprüfungen zu umgehen, insbesondere wenn Sie von Any
aus casten.
Es funktioniert, wenn Sie .toString
hinzufügen, weil Sie dann den Typ zurück in String ändern, was wirklich der Fall war. Die Lüge, welcher Typ die Daten waren, wird korrigiert.
Tags und Links scala casting type-inference type-erasure