05.03 ClassCastException
Es kommt immer dann zu einer ClassCastException
, wenn der Versuch, ein Objekt einer Klasse in ein Objekt einer anderen Klasse zu casten (umzuwandeln) fehlschlägt. Dies ist bspw. dann der Fall, wenn ein cast zwischen zwei nicht kompatiblen Typen (z. B. Integer
und String
) stattfindet.
Die ClassCastException
ist eine RuntimeException
und unchecked. Sie muss also nicht über einen try-catch Block abgefangen werden, auch wenn eine Methode über seine throws
-Klausel explizit angibt, dass eine solche Exception
geworfen werden könnte. Der Stacktrace einer ClassCastException
könnte wie folgt aussehen:
Exception in thread "main" java.lang.ClassCastException: java.lang.StringBuffer
at de.jbb.exceptions.ClassCastTest.main(ClassCastTest.java:7)
Diese Fehlermeldung sagt aus, dass in der Zeile sieben der Klasse ClassCastTest
(wir befinden uns in der Main-Methode) versucht wurde, einen StringBuffer
in ein Objekt einer nicht kompatiblen Klasse zu casten:
package de.jbb.exceptions; public class ClassCastTest { public static void main(String[] args) { System.out.println((String)getAStringValue()); } public static Object getAStringValue() { return new StringBuffer("Value"); } }
Häufige Fehlerursachen
Wie Sie aus dem letzten Beispiel erkannt haben, kommt der Fehler oftmals dann vor, wenn Sie eine Methode mit einem nicht exakt definierten Rückgabetyp (z. B. Object
) aufrufen, und den eigentlichen Rückgabetyp nicht kennen. Im oberen Fall wurde vermutet, dass die Methode getAStringValue
einen String
zurückliefert. Da aber ein StringBuffer
zurückgegeben wurde, kommt es zu diesem Fehler wenn wir versuchen in einen String
zu casten.
Erhöhte Vorsicht ist auch bei der Verwendung von nicht typisierten Collections geboten.
ArrayList integerList = new ArrayList(); integerList.add("2"); int ergebnis = 5 * ((Integer)integerList.get(0)).intValue();
Exception in thread "main" java.lang.ClassCastException: java.lang.String
at de.jbb.exceptions.ClassCastTest.main(ClassCastTest.java:13)
In diesem Fall wurde einer nicht typisierten „integerList
“ anstelle des Integers
mit dem Wert 2 ein String
mit dem Wert „2“ hinzugefügt. Bei der späteren Verarbeitung kommt es zu dem Fehler (Zeile 3).
Wie Sie wissen, ist es auch meistens besser, gegen eine Schnittstelle (ein Interface) als gegen die konkrete Implementierung (eine Klasse) zu programmieren. Sollten Sie jedoch in die Verlegenheit kommen, eine Schnittstelle in die konkrete Implementierung zu casten, müssen Sie zwingend die Klasse der Implementierung kennen. Sonst erhalten Sie auch hier eine ClassCastException
.
Set<String> set = new TreeSet<String>(); HashSet<String> hash = (HashSet<String>)set;
Exception in thread "main" java.lang.ClassCastException: java.util.TreeSet
at de.jbb.exceptions.ClassCastTest.main(ClassCastTest.java:21)
Auch bei der Verwendung von Class.cast
, also beim Casten eines Objekts in ein Objekt einer anderen Klasse über die cast
Methode der Klasse des gewünschten Zielobjekts, kann dieser Fehler auftreten. Diese Methode ist meistens nur im Zusammenhang mit Reflection notwendig.
"asdf".getClass().cast(new Integer(34));
Exception in thread "main" java.lang.ClassCastException
at java.lang.Class.cast(Unknown Source)
at de.jbb.exceptions.ClassCastTest.main(ClassCastTest.java:23
Fehlerbehebung
Vergewissern Sie sich über den konkreten Rückgabetyp einer Methode, falls dieser allgemein verfasst ist (bspw. Object
) bevor Sie casten. Sollten Sie selbst eine solche Methode schreiben, verwenden Sie Generics, sofern möglich. Selbiges gilt für die Verwendung von Collections. Verwenden Sie Generics oder, falls dies aufgrund einer zu niedrigen Java-Version nicht möglich ist, stellen Sie auf anderem Weg sicher, dass nur Objekte zulässiger Klassen in die Collection
aufgenommen werden können.
Gibt es wirklich keine Möglichkeit um festzulegen, was denn jetzt genau in eine Collection
geschrieben wird oder was eine Methode zurück gibt, überprüfen Sie zumindest mittels instanceof
vor dem Cast, ob ein solcher überhaupt möglich ist:
Object o = new Integer(42); if (o instanceof String) { System.out.println("String"); } else if (o instanceof Integer) { System.out.println("Integer"); }
Im Umgang mit Reflection gelten natürlich die selben Richtlinien zur Fehlervermeidung.