Scala unerklärliches Programmverhalten

8

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?

    
Gregory 30.10.2017, 16:58
quelle

3 Antworten

6

Das ist sicher merkwürdig.

Wenn Sie sich die folgende Anweisung in der Scala REPL ansehen:

%Vor%

Was ich denke, ist folgendes: Die 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 .

Überprüfen wir dies Schritt für Schritt in der REPL :

%Vor%

So weit, so gut. Beachten Sie, dass bisher keine Ausnahmen ausgelöst wurden, obwohl wir .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.

Nun führen wir die Zuweisung zu einer Int -Variable durch:

%Vor%

Jetzt bekommen wir die Ausnahme! Beachten Sie auch die Funktion in der Stack-Ablaufverfolgung, die sie verursacht hat: 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

Ein großer Teil des Problems ist type löschen . Vergessen Sie nicht, dass ein 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.

Schließlich in Bezug auf die 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.

%Vor%

Um zu überprüfen, ob das Objekt tatsächlich ein String ist, können Sie es wie folgt in eine Any umwandeln:

%Vor%

Es folgt eine weitere Überprüfung des Rückgabewerts von 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:

%Vor%     
Mike Allen 30.10.2017, 17:48
quelle
0

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.

    
Joe Pallas 01.11.2017 06:25
quelle
-1

Es ist ein Int , weil Sie in dieser Zeile Int anfordern:

%Vor%

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:

%Vor%

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:

%Vor%

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.

    
eje211 30.10.2017 17:31
quelle