Casting von Variablen in Java

81

Ich frage mich, ob mir jemand sagen könnte, wie Casting funktioniert. Ich verstehe, wann ich es tun sollte, aber nicht wirklich, wie es funktioniert. Bei primitiven Datentypen verstehe ich teilweise, aber wenn es um das Casting von Objekten geht, verstehe ich nicht, wie es funktioniert.

Wie kann ein Objekt vom Typ Objekt plötzlich in MyTypebeispielsweise (nur ein Beispiel) umgewandelt werden und dann alle Methoden abrufen?

user626912
quelle
Empfohlene Lektüre: Vererbung
Francesco Menzani

Antworten:

179

Das Casting in Java ist keine Zauberei. Sie teilen dem Compiler mit, dass ein Objekt vom Typ A tatsächlich vom spezifischeren Typ B ist, und erhalten so Zugriff auf alle Methoden auf B, die Sie sonst nicht gehabt hätten. Sie führen beim Casting keine Art von Magie oder Konvertierung durch, sondern sagen dem Compiler im Wesentlichen: "Vertrauen Sie mir, ich weiß, was ich tue, und ich kann Ihnen garantieren, dass dieses Objekt in dieser Zeile tatsächlich ein <Insert-Cast ist." tippe hier>. " Beispielsweise:

Object o = "str";
String str = (String)o;

Das obige ist in Ordnung, nicht magisch und alles gut. Das in o gespeicherte Objekt ist eigentlich eine Zeichenfolge, und daher können wir problemlos in eine Zeichenfolge umwandeln.

Es gibt zwei Möglichkeiten, wie dies schief gehen könnte. Erstens, wenn Sie zwischen zwei Typen in völlig unterschiedlichen Vererbungshierarchien wechseln, erkennt der Compiler, dass Sie dumm sind, und hält Sie auf:

String o = "str";
Integer str = (Integer)o; //Compilation fails here

Zweitens, wenn sie sich in derselben Hierarchie befinden, aber immer noch eine ungültige Besetzung sind, ClassCastExceptionwird zur Laufzeit a ausgelöst:

Number o = new Integer(5);
Double n = (Double)o; //ClassCastException thrown here

Dies bedeutet im Wesentlichen, dass Sie das Vertrauen des Compilers verletzt haben. Sie haben gesagt, Sie können garantieren, dass das Objekt von einem bestimmten Typ ist, und das ist es nicht.

Warum brauchst du Casting? Zunächst einmal brauchen Sie es nur, wenn Sie von einem allgemeineren Typ zu einem spezifischeren Typ wechseln. Zum Beispiel Integererbt von Number. Wenn Sie also eine Integerals speichern möchten Number, ist dies in Ordnung (da alle Ganzzahlen Zahlen sind). Wenn Sie jedoch umgekehrt vorgehen möchten, benötigen Sie eine Umwandlung - nicht alle Zahlen sind Ganzzahlen (auch) als Integer wir haben Double, Float, Byte, Long, etc.) und selbst wenn es in Ihrem Projekt oder das JDK nur eine Unterklasse ist, könnte jemand eine andere leicht zu erstellen und verteilen, so dass Sie auch keine Garantie haben , wenn Sie denken , dass es eine einzige offensichtliche Wahl !

In Bezug auf die Verwendung für das Casting sehen Sie in einigen Bibliotheken immer noch die Notwendigkeit dafür. Vor Java-5 wurde es häufig in Sammlungen und verschiedenen anderen Klassen verwendet, da alle Sammlungen daran arbeiteten, Objekte hinzuzufügen und dann das Ergebnis zu übertragen, dass Sie die Sammlung wieder verlassen haben. Mit dem Aufkommen von Generika ist jedoch ein Großteil der Verwendung für das Casting weggefallen - es wurde durch Generika ersetzt, die eine viel sicherere Alternative darstellen, ohne das Potenzial für ClassCastExceptions (in der Tat, wenn Sie Generika sauber verwenden und es ohne Warnungen kompiliert wird). Sie haben die Garantie, dass Sie niemals eine ClassCastException erhalten.)

Michael Berry
quelle
Ich danke Ihnen für Ihre Erklärung. Wenn ich das richtig verstehe, weiß ich es wahrscheinlich nicht. Wenn Sie ein Objekt umwandeln, sagen Sie dem Compiler nur, dass ich weiß, dass das Objekt auf dieser Speicheradresse weiß, wie man auf diese Methoden usw. reagiert. Verweigern Sie mich also nicht? Ist das korrekt?
user626912
1
@ user626912 Art von - denken Sie jedoch nicht an Speicheradressen. Sie teilen dem Compiler nicht nur mit, dass das angegebene Objekt einer Schnittstelle entspricht und daher Implementierungen bestimmter Methoden enthält (Sie könnten mit denselben Methoden ein völlig anderes Objekt erstellen, und das Casting würde nicht unbedingt funktionieren.) Sie teilen dem Compiler dies mit Das Objekt eines Typs ist tatsächlich ein spezifischerer Typ. Daher können Sie die für dieses spezifischere Objekt verfügbaren Methoden verwenden. Informieren Sie sich über Polymorphismus, wenn Sie dies noch nicht getan haben. Es kann hilfreich sein, einige Dinge klarer zu machen.
Michael Berry
Ich bezweifle Ihre Aussage "Sie brauchen sie nur, wenn Sie von einem allgemeineren Typ zu einem spezifischeren Typ wechseln". ((Objekt) gpsLastLoc.getLatitude ()). GetClass (). GetSimpleName () gibt zur Laufzeit den "doppelten" Namen zurück. Dies ist das Beispiel für die Umwandlung von einem spezifischeren Typ in einen allgemeineren Typ.
Obst
1
@ 林果 皞 In diesem Fall verwenden Sie nur Casting, um ein Primitiv zu fördern - dies ist nicht dasselbe wie ein allgemeinerer Typ und eine sehr seltsame Art, Dinge zu tun. Der normalere (bessere) Weg wäre Double.valueOf(gpsLastLoc.getLatitude()).getClass().getSimpleName(). In beiden Fällen sollten Sie die Klasse eines Grundelements niemals dynamisch abrufen müssen, da getLatitude()Sie immer wissen, dass ein DoubleObjekt , wenn es ein Doppelprimitiv zurückgibt, zu einem Objekt heraufgestuft wird.
Michael Berry
Ich finde es toll, wie Sie das sagen, dass der Code "das Vertrauen des Compilers verletzt" hat. (Ps. Sie haben einen Tippfehler, Sie haben "Sie sind" anstelle von "Sie haben" geschrieben.)
Jason L.
7

Casting funktioniert eigentlich nicht immer. Wenn das Objekt nicht instanceofdie Klasse ist, in die Sie es umwandeln, erhalten Sie zur ClassCastExceptionLaufzeit eine.

Sandstern
quelle
5

Angenommen, Sie wollten a Stringin a Fileumwandeln (ja, das ergibt keinen Sinn), Sie können es nicht direkt umwandeln, da die FileKlasse kein Kind und kein Elternteil der StringKlasse ist (und der Compiler sich beschwert).

Aber Sie könnten Ihre Stringzu werfen Object, weil a Stringein Object( Objectist Elternteil). Dann könnten Sie dieses Objekt in Filea umwandeln, da eine Datei eine ist Object.

Alle Ihre Vorgänge sind also zum Zeitpunkt der Kompilierung unter dem Gesichtspunkt der Eingabe "legal", aber dies bedeutet nicht, dass sie zur Laufzeit funktionieren!

File f = (File)(Object) "Stupid cast";

Der Compiler lässt dies zu, auch wenn es keinen Sinn ergibt, stürzt jedoch zur Laufzeit ab, mit der folgenden Ausnahme:

Exception in thread "main" java.lang.ClassCastException:
    java.lang.String cannot be cast to java.io.File
Christophe Roussy
quelle
3

Das Casting einer Referenz funktioniert nur, wenn es sich um einen instanceofsolchen Typ handelt. Sie können keine zufälligen Referenzen erstellen. Außerdem müssen Sie mehr darüber lesen Casting Objects.

z.B

String string = "String";

Object object = string; // Perfectly fine since String is an Object

String newString = (String)object; // This only works because the `reference` object is pointing to a valid String object.
Fragen
quelle
3

Der richtige Weg ist folgender:

Integer i = Integer.class.cast(obj);

Die Methode cast()ist eine viel sicherere Alternative zum Casting zur Kompilierungszeit.

yegor256
quelle