liveBook · Manning

ClassCastException

As explained in 100 Java Mistakes and How to Avoid Them

ClassCastException is the Java runtime exception thrown when a value cannot be cast to the requested reference type, including when identically named classes come from different class loaders.

ClassCastException occurs when Java converts a value to a requested reference type and the value is not compatible with that type. It arises from an explicit cast written in source or an implicit cast inserted for a generic type. section 5.3 section 5.3.2

An unconditional cast discards useful static-type information. If a method really accepts only String, declare a String parameter instead of Object; where an Object signature is required, reject incompatible input before delegating. At public boundaries, this turns a late cast failure into an earlier, clearer validation failure. section 5.3.1 section 5.5

if (obj instanceof MyType myType) {
  … // Use myType.
}

Pattern matching for instanceof checks the runtime type and introduces a correctly typed variable only on the successful branch, avoiding a separate explicit cast. section 5.3.1

Avoid explicit-cast failures by keeping the type contract visible:

  • Guard a necessary cast with instanceof; Java 16 pattern matching removes the repeated type name and never throws ClassCastException for the pattern test. section 5.3.1
  • Use getClass() checks carefully in equals() implementations, and generate equals() rather than copying a similar implementation with a stale cast type. section 5.3.1
  • Prefer a generic interface such as Processor<T> when the operation has one input type, rather than accepting Object and casting internally. section 5.3.1
  • Treat classes with the same name from separate class loaders as different classes when diagnosing an unexpected failure. section 5.5

Generic code can fail far from the mistake. At runtime, type arguments are erased: List<String> is simply List to the virtual machine, and list.get(0) returns Object. The compiler inserts a cast when assigning that result to String. An unchecked cast or raw-type write can therefore corrupt a list now and trigger ClassCastException later at an apparently safe read. section 5.3.2 section 5.5

A delayed generic failure

A List<String> can be viewed as List<?> and then unsafely cast to List<Object>; adding an Integer succeeds after erasure. Later, printFirstItem(List<String>) fails when its read is implicitly cast to String. Avoid the unchecked conversion; express read-only variance with wildcard types, and use checked or unmodifiable wrappers when appropriate. section 5.3.2 section 5.5

How 100 Java Mistakes and How to Avoid Them covers it

How deep it goes
Explains explicit checks and compiler-inserted generic casts down to runtime type erasure, while deferring pattern details to Java 16 references.
Examples
Uses Object-to-String processing, equals() casting, pattern instanceof, and a corrupted List<String> whose failure is delayed.
What it assumes
Assumes Java reference types and casts, then explains generic-list failures through erased runtime type arguments.
What you can do after
Diagnose/debug with it by locating unsafe explicit or erased generic conversions and replacing them with checked type contracts.
How it connects
Connects instanceof, equals(), generics, type erasure, raw types, wildcard types, wrappers, and class loaders.

Also explained in

Compare how 2 books explain ClassCastException →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Java Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.