sollte dieser Code keine ClassCastException erzeugen

8

Der folgende Code wird kompiliert und erfolgreich ohne Ausnahme ausgeführt

%Vor%

sollte nicht die Zeile ArrayList<SuperSample> ssList = (ArrayList<SuperSample>)o; eine ClassCastException erzeugen?

während der folgende Code einen Fehler beim Kompilieren-Zeitfehler erzeugt, um Heap-Verschmutzungen zu vermeiden, sollte der oben erwähnte Code nicht eine ähnliche Vorbeugung zur Laufzeit enthalten?

%Vor%

BEARBEITEN:

Wenn Type Erasure der Grund dafür ist, sollten nicht zusätzliche Mechanismen vorhanden sein, die verhindern, dass ein ungültiges Objekt zur Liste hinzugefügt wird? zum Beispiel

%Vor%

dann warum

%Vor%

ist nicht gemacht, um eine Ausnahme zu werfen?

    
Thirumalai Parthasarathi 22.11.2013, 11:16
quelle

5 Antworten

1

In Ihrem Codebeispiel

%Vor%
  

Sollte nicht die letzte Zeile ein ClassCastException erzeugen?

Nein. Diese Ausnahme wird von der JVM ausgelöst, wenn inkompatible Typen zur Laufzeit erkannt werden. Wie andere bereits festgestellt haben, liegt dies an löschung von generischen Typen. Das heißt, generische Typen sind nur dem Compiler bekannt. Auf der JVM-Ebene sind die Variablen alle vom Typ ArrayList (die Generika wurden gelöscht), daher gibt es zur Laufzeit kein ClassCastException .

Nebenbei: Statt einer lokalen Zwischenvariable vom Typ Object zuzuweisen, besteht eine prägnantere Möglichkeit für diese Zuweisung darin, rough zu durchlaufen:

%Vor%

wobei ein "roher" Typ die gelöschte Version eines generischen Typs ist.

  

Sollte es keine zusätzlichen Mechanismen geben, die verhindern, dass ein ungültiges Objekt zur Liste hinzugefügt wird?

Ja, da sind. Der erste Mechanismus ist die Kompilierzeitprüfung. In Ihrer eigenen Antwort haben Sie den richtigen Ort in der Java-Sprachspezifikation gefunden, in dem Überlastung , was der Begriff für ein ungültiges Objekt ist in der Liste auftreten. Das Geldzitat aus diesem Abschnitt, ganz unten, ist

  

Wenn keine Operation ausgeführt wird, für die eine ungeprüfte Kompilierungswarnung ausgegeben werden muss, und keine unsicheren Aliasbefehle für Arrayvariablen mit nicht-veränderbaren Elementtypen auftreten, kann keine Heap-Verunreinigung auftreten.

Der gesuchte Mechanismus befindet sich also im Compiler und der Compiler benachrichtigt Sie über Kompilierungswarnungen. Sie haben diesen Mechanismus jedoch mithilfe der Annotation @SuppressWarnings deaktiviert. Wenn Sie diese Anmerkung entfernen würden, erhalten Sie eine Compiler-Warnung in der betreffenden Zeile. Wenn Sie die Verschmutzung durch Heaps unbedingt vermeiden möchten, verwenden Sie nicht @SuppressWarnings und fügen Sie der Befehlszeile -Xlint:unchecked -Werror die Optionen javac hinzu.

Der zweite Mechanismus ist die Laufzeitprüfung, die die Verwendung eines der geprüften Wrapper erfordert. Ersetzen Sie die Initialisierung von sList durch Folgendes:

%Vor%

Dadurch wird ClassCastException an dem Punkt ausgelöst, an dem der Liste ein SuperSample hinzugefügt wurde.

    
Stuart Marks 23.11.2013, 08:05
quelle
6

Nein, zur Laufzeit sollten beide Listen nicht den gleichen ArrayList-Typ haben. Dies wird löschen genannt. Generische Parameter sind nicht Teil der kompilierten Klasse, sie werden alle während der Kompilierung gelöscht. Aus Sicht von JVM entspricht Ihr Code:

%Vor%

Grundsätzlich vereinfachen Generika die Entwicklung nur, indem sie Kompilierzeitfehler und -warnungen erzeugen, aber sie beeinflussen die Ausführung überhaupt nicht.

BEARBEITEN:

Nun, das Basiskonzept dahinter ist Readable Type . Ich empfehle dringend, dieses Handbuch zu lesen:

  

Ein verifizierbarer Typ ist ein Typ, dessen Typinformationen vollständig verfügbar sind   zur Laufzeit. Dies beinhaltet Primitive, nicht-generische Typen, Rohtypen,   und Aufrufe von ungebundenen Platzhaltern.

     

Nicht umformbare Typen sind Typen, bei denen Informationen entfernt wurden   Kompilierzeit nach Typ löschen

Um kurz zu sein: Arrays sind ratifizierbar und generische Collections nicht. Wenn Sie also etw im Array speichern, wird type von JVM überprüft, da der Typ des Arrays zur Laufzeit vorhanden ist. Array stellt nur ein Stück Erinnerung dar, während Sammlung eine gewöhnliche Klasse ist, die irgendeine Art von Implementierung haben kann. Zum Beispiel kann es Daten in db oder auf der Festplatte unter der Haube speichern. Wenn Sie tiefer gehen möchten, empfehle ich, Java Generics and Collections zu lesen.

    
Mikhail 22.11.2013 12:24
quelle
1

Der Schlüssel hier, um Ihre Frage zu beantworten, ist Type Erasure in Java

Sie haben eine Warnung zur Kompilierungszeit für Ihren ersten Fall und nicht in der Sekunde wegen Ihrer Indirektion durch ein Objekt, die den Compiler daran hindert, Sie zu warnen (ich diese Warnung) wird ausgelöst, wenn ein parametrisierter Typ in einen anderen umgewandelt wird, der in Ihrem zweiten Fall nicht ausgeführt wird, wenn jemand bestätigen kann, dass ich mich darüber freuen würde). Und dein Code läuft, weil am Ende sList ssList und o alle ArrayList

sind     
benzonico 22.11.2013 12:26
quelle
1

Ich denke, dass dies ClassCastException wegen Rückwärtskompatibilitätsproblem in Java nicht erzeugen kann.

Generische Informationen sind nicht im Bytecode enthalten (der Compiler erhält während der Kompilierung RIDs).

Stellen Sie sich ein Szenario vor, dass Sie in Ihrem Projekt einen alten Legacy-Code (eine alte Bibliothek, die in Java 1.4 geschrieben wurde) verwenden und die generische Liste an eine Methode in diesem Legacy-Code übergeben. Sie können dies tun.

Rechtzeitig vor Generics wurde Legacy-Code erlaubt, alles (außer Primitive) in eine Sammlung zu legen. Daher kann dieser Legacy-Code keine ClassCastException erhalten, auch wenn er versucht, String in die Liste & lt; Integer & gt; Aus der Sicht des Legacy-Codes ist es nur List.

Dieses seltsame Verhalten ist also eine Folge des Löschens von Typen und der Rückwärtskompatibilität in Java.

BEARBEITEN:

Sie erhalten ArrayStoreException für Arrays, weil die JVM zur Laufzeit den Typ von Arrays kennt und Sie keine Ausnahme für Collections erhalten, weil Type Erasure und dieses Abwärtskompatibilitätsproblem JVM den Typ der Sammlung zur Laufzeit nicht kennt.

Sie können zu diesem Thema im Buch "SCJP Sun® Certified Programmer für Java ™ 6 Study Guide" in Kapitel 7 "Generics and Collections"

nachlesen     
bary 22.11.2013 12:41
quelle
0

Aus der JLS (4.12. 2)

Es ist möglich, dass sich eine Variable eines parametrisierten Typs auf ein Objekt bezieht, das nicht definiert ist dieses parametrisierten Typs. Diese Situation wird als Haufenverschmutzung bezeichnet. Diese Situation kann nur auftreten, wenn das Programm eine Operation ausführt, die einen Anstieg verursachen würde zu einer ungeprüften Warnung bei der Kompilierung.

Zum Beispiel der Code:

%Vor%

führt zu einer ungeprüften Warnung, weil es nicht möglich ist, Zeit (innerhalb der Grenzen der Kompilierzeit Typprüfregeln) oder zur Laufzeit, ob Die Variable l bezieht sich tatsächlich auf List<String> . Wenn der obige Code ausgeführt wird, tritt Heap-Verschmutzung auf, wie die Variable ls, die zu a erklärt wird List<String> , bezieht sich auf einen Wert, der nicht tatsächlich ein List<String> ist. Das Problem kann zur Laufzeit nicht identifiziert werden, da Typvariablen nicht verdinglicht werden. und somit enthalten Instanzen zur Laufzeit keine Informationen über den tatsächlichen Typ Parameter verwendet, um sie zu erstellen.

    
quelle