Definition
- ClassCastException is thrown at runtime when you cast an object to a type it is not:
(Integer) stringObj. - Three main causes: (1) Invalid downcast without
instanceofcheck. (2) Heap pollution from mixing raw types and generics. (3)toArray()returningObject[]which cannot be cast toString[]. - Fix: use
instanceofbefore casting (Java 16 pattern matching:if (obj instanceof String s)), use proper generics (no raw types), usetoArray(new String[0])ortoArray(String[]::new).
At National Bank of India, you have a safe deposit box labeled "Account" (reference type). Inside is actually a SavingsAccount. You try to open it as a LoanAccount and the system rejects the access. Always check the contents before treating them as a specific type.
Understanding the Concepts
What causes ClassCastException?
The most common cause is an invalid downcast where the actual object type does not match the target type.
package com.bank.demo;
public class ClassCastDemo {
// CAUSE 1: invalid downcast
public static void invalidDowncast() {
Object obj = "Hello";
try {
Integer num = (Integer) obj; // String is NOT Integer
} catch (ClassCastException e) {
System.out.println("CCE: " + e.getMessage());
}
}
// FIX: instanceof check (Java 16 pattern matching)
public static void safeDowncast() {
Object obj = "Hello";
if (obj instanceof String s) {
System.out.println("Safe: " + s.toUpperCase());
} else {
System.out.println("Not a String");
}
}
// CAUSE 2: toArray() returns Object[]
public static void toArrayMisuse() {
var accounts = java.util.List.of("SAV2001", "CUR3001");
Object[] objArray = accounts.toArray();
try {
String[] strArray = (String[]) objArray; // CCE!
} catch (ClassCastException e) {
System.out.println("toArray CCE: Object[] cannot cast to String[]");
}
// FIX:
String[] correct = accounts.toArray(new String[0]);
System.out.println("Fixed: " + java.util.Arrays.toString(correct));
}
public static void main(String[] args) {
invalidDowncast();
safeDowncast();
toArrayMisuse();
}
}
CCE: class java.lang.String cannot be cast to class java.lang.Integer
Safe: HELLO
toArray CCE: Object[] cannot cast to String[]
Fixed: [SAV2001, CUR3001]
What is heap pollution?
Heap pollution occurs when raw types allow incompatible objects into a generic collection. The CCE happens at get(), not at insertion.
package com.bank.demo;
import java.util.ArrayList;
import java.util.List;
public class HeapPollutionDemo {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
List rawList = new ArrayList(); // raw type, no generic
rawList.add("Rajesh");
rawList.add(42); // Integer added, no compile error
List<String> names = rawList; // unchecked assignment
System.out.println("First: " + names.get(0)); // OK
try {
String second = names.get(1); // CCE! Integer, not String
} catch (ClassCastException e) {
System.out.println("Heap pollution: " + e.getMessage());
}
// FIX: never use raw types
List<String> safe = new ArrayList<>();
safe.add("Rajesh");
// safe.add(42); // COMPILE ERROR with generics
System.out.println("Safe: " + safe.get(0));
}
}
First: Rajesh
Heap pollution: class java.lang.Integer cannot be cast to class java.lang.String
Safe: Rajesh
What should a senior engineer know?
- Java 21 sealed + switch. Sealed interfaces with pattern matching switch eliminate downcasting entirely. The compiler ensures exhaustive handling.
- Generics at runtime. Due to type erasure,
ListandListare the same type at runtime. Generics prevent CCE at compile time, not runtime. - Collections.checkedList(). Wraps a list with runtime type checks:
Collections.checkedList(list, String.class)throws CCE on insertion of wrong type.
What are common interview follow-up questions?
- Why does toArray() return Object[]? (Due to type erasure, the list does not know its element type at runtime.)
- How does instanceof pattern matching help? (Check and cast in one step, no separate cast statement.)
- What is heap pollution? (Raw types allowing wrong types into generic collections.)
What are common mistakes?
A common mistake is not understanding the concept fully before using it in production banking applications.
Wrong approach:
package com.bank.service;
public class TransferService {
public void transfer(String fromAccount, String toAccount, double amount) {
// WRONG: using double for money
double balance = getBalance(fromAccount);
balance = balance - amount;
}
private double getBalance(String id) { return 10000.0; }
}
Correct approach:
package com.bank.service;
import java.math.BigDecimal;
public class TransferService {
public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
// CORRECT: using BigDecimal for money
BigDecimal balance = getBalance(fromAccount);
balance = balance.subtract(amount);
}
private BigDecimal getBalance(String id) { return new BigDecimal("10000.00"); }
}
Always use BigDecimal for monetary calculations in banking systems to avoid floating-point precision errors.
What are the edge cases?
Edge cases can cause unexpected behavior in banking applications. Here are some to watch for.
package com.bank.edge;
public class EdgeCaseDemo {
public static void main(String[] args) {
// Edge case 1: null handling
String accountId = null;
try {
System.out.println(accountId.length());
} catch (NullPointerException e) {
System.out.println("Null account ID");
}
// Edge case 2: empty vs null
String emptyId = "";
System.out.println(emptyId.isEmpty()); // true
System.out.println(emptyId == null); // false
// Edge case 3: boundary values
java.math.BigDecimal zero = java.math.BigDecimal.ZERO;
java.math.BigDecimal negativeBalance = new java.math.BigDecimal("-0.01");
System.out.println(zero.compareTo(negativeBalance) > 0); // true
}
}
Null account ID
true
false
true
Always test with null values, empty strings, boundary values, and negative amounts in banking applications.
Visual Overview
ClassCastException causes:
1. Invalid downcast: (Integer) stringObj → CCE
Fix: if (obj instanceof Integer i) { ... }
2. Heap pollution: raw List + generic List → CCE at get()
Fix: always use generics, never raw types
3. toArray() misuse: (String[]) list.toArray() → CCE
Fix: list.toArray(new String[0])
Key Takeaway
ClassCastException occurs at runtime when the actual object type does not match the cast. The three main causes are invalid downcasts (casting without instanceof check), heap pollution from raw types (mixing generic and raw collections lets incompatible objects sneak in), and toArray() returning Object[] which cannot be cast to String[].
Prevention: use Java 16 pattern matching if (obj instanceof String s) for safe downcasting. Never use raw types. Use toArray(new String[0]) or toArray(String[]::new). Modern Java with sealed interfaces and switch pattern matching eliminates the need for manual downcasting entirely.
At National Bank of India, the transaction processor blindly cast (Deposit) transaction for every incoming transaction. When a Withdrawal arrived, ClassCastException crashed the processor and 500 transactions were stuck. Fix: pattern matching or instanceof before every downcast.
Remember one sentence: always check with instanceof before downcasting, never use raw types, and use toArray(new String[0]) instead of casting toArray().
---