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.

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.