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

