Introduction to Runtime Exceptions
Runtime exceptions are exceptions which can not be checked at compile time. In Java, there are a myriad of classes derived from the RuntimeException
class [1], all of which represent unchecked exceptions that need to be carefully considered and managed. Despite being less serious and critical than the unchecked runtime errors [2], these exceptions can still be very problematic and cause unexpected issues at runtime, especially if necessary precautions aren’t taken and relevant exception handling mechanisms aren’t put in place.
 
What is ClassCastException and When does it Happen?
As its name implies, ClassCastException
is an exception that happens when the JVM tries to cast an object to a class (or in some instances, an interface) and fails. This relates to explicit type casting [3] and the reason the cast fails can be traced to an attempt at downcasting an object to a class of which it is not an instance, or to an interface which it does not implement.
ClassCastException
is a subclass of the RuntimeException
class which means it is an unchecked, runtime exception [4]. This exception can not be checked at compile-time because the compiler has no way of knowing whether the object is actually an instance of the target subclass, or if it is an instance of a subclass that implements the target interface. Consequently, if either of these scenarios is encountered at runtime, Java will throw the ClassCastException
exception.
Parent parent = new Child();
/*...*/
Child c = (Child) parent; // is parent actually an instance of Child?
IFace i = (IFace) parent; // Is parent an instance of a subclass that implements IFace?
The only scenario where the compiler is able to detect invalid type casts of this kind is when the source type is a final
class and it neither extends nor implements the target type, because it is known in advance that the final
class does not have any subtypes, i.e., it cannot be subclassed [5].
String s = "s";
IFace i = (IFace) s; // compilation error (the String class is final)
 
How to handle ClassCastException
To prevent the ClassCastException
exception, one should be careful when casting objects to a specific class or interface and ensure that the target type is a child of the source type, and that the actual object is an instance of that type. To help achieve type safety and catch these issues at compile time, two builtin Java mechanisms are available:
- the
instanceof
operator, and - Generics.
 
ClassCastException Examples
To better understand ClassCastException
, consider the following Java class hierarchy:
class X {/*...*/}
class Y extends X {/*...*/}
class Z extends X {/*...*/}
Object o = new Z(); // OK
X x = new Y(); // OK
Y y = (Y) x; // OK
y = (Y) o; // Will throw ClassCastException
y = (Y) new X(); // Will throw ClassCastException
Z z = (Z) x; // Will throw ClassCastException
The resulting scenarios can be summarized as follows:
- It is possible to cast an instance of
X
,Y
, orZ
, toObject
, since all Java classes implicitly inherit thejava.lang.Object
class [6]. - It is possible to cast an instance of
Y
orZ
toX
, because they are both subtypes ofX
. - It is possible to cast an instance of type
X
to typeY
(orZ
) ONLY if the original object is of typeY
(orZ
), due to polymorphism [7]. - It is impossible to cast an instance of
Y
toZ
despite the fact that they are both derived fromX
, becauseY
andZ
are unique types with distinct states and behaviors.
Complete examples and ways to deal with ClassCastException
are presented below.
 
Using the instanceof operator
Java’s instanceof
operator is a binary operator used to test whether the object is an instance of a specific class, or a class that implements a specific interface [8]. When used in the appropriate context, this operator can prevent the ClassCastException
exception from occurring. The code example below shows how trying to cast an instance of Phone
to a subclass of Phone
(Smartphone
) throws the ClassCastException
exception.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package rollbar;
class Phone {}
interface Wireless {
default void charge() {
System.out.println("Phone is charging");
}
}
class Smartphone extends Phone implements Wireless {}
public class ClassCastExceptionExample {
private static final Phone myPhone = new Phone();
public static void main(String... args) {
Smartphone wirelessPhone = (Smartphone) myPhone;
wirelessPhone.charge();
}
}
Exception in thread "main" java.lang.ClassCastException: class rollbar.Phone cannot be cast to class rollbar.Smartphone
at rollbar.ClassCastExceptionExample.main(ClassCastExceptionExample.java:19)
Casting an object to an interface is also a valid polymorphic operation, so one might try to cast the myPhone
variable to a Wireless
instance instead. However, since myPhone
is not an instance of any class that implements Wireless
, the ClassCastException
is thrown again.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package rollbar;
class Phone {}
interface Wireless {
default void charge() {
System.out.println("Phone is charging");
}
}
class Smartphone extends Phone implements Wireless {}
public class ClassCastExceptionExample {
private static final Phone myPhone = new Phone();
public static void main(String... args) {
Wireless wirelessPhone = (Wireless) myPhone;
wirelessPhone.charge();
}
}
Exception in thread "main" java.lang.ClassCastException: class rollbar.Phone cannot be cast to class rollbar.Wireless
at rollbar.ClassCastExceptionExample.main(ClassCastExceptionExample.java:19)
The solution here is to use the instanceOf
operator which will enforce a safe type cast, as shown below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package rollbar;
class Phone {}
interface Wireless {
default void charge() {
System.out.println("Phone is charging");
}
}
class Smartphone extends Phone implements Wireless {}
public class ClassCastExceptionExample {
private static final Phone myPhone = new Phone();
public static void main(String... args) {
if (myPhone instanceof Smartphone smartphone) {
smartphone.charge();
} else {
System.out.println("Phone cannot be charged.");
}
}
}
Phone cannot be charged.
The same concept applies to interfaces:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package rollbar;
class Phone {}
interface Wireless {
default void charge() {
System.out.println("Phone is charging");
}
}
class Smartphone extends Phone implements Wireless {}
public class ClassCastExceptionExample {
private static final Phone myPhone = new Phone();
public static void main(String... args) {
if (myPhone instanceof Wireless wirelessPhone) {
wirelessPhone.charge();
} else {
System.out.println("Phone cannot be charged.");
}
}
}
Phone cannot be charged.
Since myPhone
is neither an instance of Smartphone
nor an instance of a class that implements Wireless
, the instanceOf
operator inside the if
statement evaluates to false, and the corresponding else
clause is executed.
On the other hand, if an object passes the instanceOf
check, then it can be safely cast to the specified type. This can be observed in the example below where the myPhone
variable is an actual instance of the Smartphone
class (as initialized on line 16).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package rollbar;
class Phone {}
interface Wireless {
default void charge() {
System.out.println("Phone is charging");
}
}
class Smartphone extends Phone implements Wireless {}
public class ClassCastExceptionExample {
private static final Phone myPhone = new Smartphone();
public static void main(String... args) {
if (myPhone instanceof Wireless wirelessPhone) {
wirelessPhone.charge();
} else {
System.out.println("Phone cannot be charged.");
}
}
}
Phone is charging.
As a side note, older versions of Java which don’t support pattern matching for the instanceOf
operator [9] will require an extra step to cast the object manually, as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package rollbar;
class Phone {}
interface Wireless {
default void charge() {
System.out.println("Phone is charging");
}
}
class Smartphone extends Phone implements Wireless {}
public class ClassCastExceptionExample {
private static final Phone myPhone = new Smartphone();
public static void main(String... args) {
if (myPhone instanceof Wireless) {
((Wireless) myPhone).charge();
} else {
System.out.println("Phone cannot be charged.");
}
}
}
Phone is charging.
 
Using Generics & Parameterized Types
Introduced in Java 5, Generics are a very important addition to Java’s type system which brought compile-time type safety and eliminated the need for the tedious type casting when working with the Collections Framework [10]. This mechanism allows programmers to implement generic data structures and algorithms that are type-safe, and it allows Java compilers to perform strong type checking and detect related issues at compile-time.
A parameterized type is an instantiation of a generic type with an actual type argument. The code below shows how the use of raw, unparameterized collections such as List
s can easily lead to the ClassCastException
being triggered. This is because unparameterized collections default to the Object
type, so nothing prevents a program or an API from inserting an instance of an unexpected type into a collection. The example below shows how inserting and later trying to cast the string “200” into a List
instance throws the ClassCastException
exception.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package rollbar;
import java.util.ArrayList;
import java.util.List;
public class ClassCastExceptionGenerics {
private static final List integerList = new ArrayList<>();
public static void main(String... args) {
integerList.add(100);
integerList.add(150);
integerList.add("200");
integerList.forEach(o -> printRoot((Integer) o));
}
public static void printRoot(Integer number) {
if (number != null) {
System.out.println(Math.sqrt(number));
}
}
}
Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer
at rollbar.ClassCastExceptionGenerics.lambda$main$0(ClassCastExceptionGenerics.java:15)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at rollbar.ClassCastExceptionGenerics.main(ClassCastExceptionGenerics.java:15)
Using Generics to make the List
parameterized restricts the types of objects the list can hold to valid instances of Integer
, which in turn makes any attempt to insert any other, incompatible type in the list detectable at compile-time, as shown in the revised example below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package rollbar;
import java.util.ArrayList;
import java.util.List;
public class ClassCastExceptionGenericsFixed {
private static final List<Integer> integerList = new ArrayList<>();
public static void main(String... args) {
integerList.add(100);
integerList.add(150);
integerList.add("200");
integerList.forEach(o -> printRoot((Integer) o));
}
public static void printRoot(Integer number) {
if (number != null) {
System.out.println(Math.sqrt(number));
}
}
}
ClassCastExceptionGenerics.java:13: error: incompatible types: String cannot be converted to Integer
integerList.add("200");
^
1 error
Furthermore, using parameterized types to instantiate Generics eliminates the need to cast collection objects manually, so a working version of the example above could look something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package rollbar;
import java.util.ArrayList;
import java.util.List;
public class ClassCastExceptionGenerics {
private static final List<Integer> integerList = new ArrayList<>();
public static void main(String... args) {
integerList.add(100);
integerList.add(150);
integerList.add(200);
integerList.forEach(o -> printRoot(o));
}
public static void printRoot(Integer number) {
if (number != null) {
System.out.println(Math.sqrt(number));
}
}
}
10.0
12.24744871391589
14.142135623730951
 
Conclusion
Runtime exceptions are an inevitable evil that all Java programmers have to face at some point. One of these exceptions is the ClassCastException
which is thrown whenever there is an attempt to cast an object to a class or an interface the object is incompatible with. As with other runtime exceptions, being prudent is important and pays off in the long run. This article explains what causes the ClassCastException
by diving into Java’s type casting rules, and it shows how to prevent and effectively deal with this exception by relying on the instanceof
operator and using generic, parameterized types when the situation calls for it.
 
Track, Analyze and Manage Errors With Rollbar
Managing Java errors and exceptions in your code is challenging. It can make deploying production code an unnerving experience. Being able to track, analyze, and manage errors in real-time can help you to proceed with more confidence. Rollbar automates error monitoring and triaging, making fixing Java errors easier than ever. Sign Up Today!
 
References
[1] Oracle, 2021. RuntimeException (Java SE 17 & JDK 17). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/RuntimeException.html. [Accessed Jan. 21, 2022]
[2] Oracle, 2021. Error (Java SE 17 & JDK 17). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Error.html. [Accessed Jan. 21, 2022]
[3] Rollbar, 2022. How to Handle the Incompatible Types Error in Java. Rollbar Editorial Team. [Online]. Available: https://rollbar.com/blog/how-to-handle-the-incompatible-types-error-in-java/. [Accessed Jan. 21, 2022]
[4] Oracle, 2021. Unchecked Exceptions — The Controversy (The Java™ Tutorials > Essential Java Classes > Exceptions). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html. [Accessed Jan. 21, 2022]
[5] Oracle, 2021. Writing Final Classes and Methods (The Java™ Tutorials > Learning the Java Language > Interfaces and Inheritance). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/javase/tutorial/java/IandI/final.html. [Accessed Jan. 21, 2022]
[6] Oracle, 2021. Inheritance (The Java™ Tutorials > Learning the Java Language > Interfaces and Inheritance). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html. [Accessed Jan. 21, 2022]
[7] Oracle, 2021. Polymorphism (The Java™ Tutorials > Learning the Java Language > Interfaces and Inheritance). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html. [Accessed Jan. 21, 2022]
[8] Oracle, 2021. Equality, Relational, and Conditional Operators (The Java™ Tutorials > Learning the Java Language > Language Basics). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op2.html. [Accessed Jan. 21, 2022]
[9] G. Bierman, 2021. JEP 394: Pattern Matching for instanceof. Oracle and/or its affiliates. [Online]. Available: https://openjdk.java.net/jeps/394. [Accessed Jan. 21, 2022]
[10] Oracle, 2021. Why Use Generics? (The Java™ Tutorials > Learning the Java Language > Generics (Updated)). Oracle and/or its affiliates. [Online]. Available: https://docs.oracle.com/javase/tutorial/java/generics/why.html. [Accessed Jan. 21, 2022]