<code>ClassCastException</code> in Java

ClassCastException is thrown when you cast an object to a type it doesn't actually implement or extend. It's an unchecked exception β€” always a bug, caused by a mismatch between what the code claims and what the object really is.

The cause

Object o = "hello";
Integer i = (Integer) o;          // ClassCastException at runtime
// java.lang.ClassCastException: class java.lang.String
//   cannot be cast to class java.lang.Integer

Prevent with instanceof

// Classic
if (o instanceof Integer) {
    Integer i = (Integer) o;
    ...
}

// Pattern matching for instanceof (Java 16+)
if (o instanceof Integer i) {
    System.out.println(i + 1);     // `i` is in scope and already the right type
}

Pattern matching for switch (Java 21+)

String describe(Object o) {
    return switch (o) {
        case Integer i  -> "int " + i;
        case String s   -> "string " + s;
        case null       -> "null";
        default         -> "other";
    };
}

The raw-types trap

// Using raw types bypasses the compile-time check
List list = new ArrayList();
list.add("hi");
list.add(1);
List<String> strings = list;      // unchecked warning
String s = strings.get(1);         // ClassCastException β€” runtime

Always use parameterised generics (List<String>), not raw types.

Downcasting collections

Object obj = service.result();
// ❌ Don't blindly cast
List<User> users = (List<User>) obj;   // unchecked

// βœ… Check, then copy (safe but more work)
if (obj instanceof List<?> l) {
    List<User> users = new ArrayList<>();
    for (Object e : l) if (e instanceof User u) users.add(u);
}

Common mistakes

  • Catching ClassCastException β€” never the right fix. Use instanceof.
  • Unchecked casts of generics β€” due to type erasure, (List<User>) obj is a lie to the compiler. Check at runtime.
  • Casting to a subclass without checking β€” good code rarely needs downcasts. Reconsider the design if you do.

Related

Pillar: Java exceptions. See also generics and instanceof.