14 Safe Casting with instanceof and switch
You can use the instanceof operator to check at run time
whether a cast from one type to another is safe. A cast or conversion is safe if
there's no loss of information or exception thrown during the cast or conversion. You can
use the instanceof operator to test whether a cast is safe between two
reference types or two primitive types. In addition, conversion safety affects how
switch statements and expressions behave.
Note:
The ability to use theinstanceof operator to test whether a cast is safe between two
primitive types is a preview feature. A preview feature is a feature whose design,
specification, and implementation are complete, but is not permanent. A preview feature
may exist in a different form or not at all in future Java SE releases. To compile and
run code that contains preview features, you must specify additional command-line
options. See Preview Language and VM
Features.
See JEP 455: Primitive Types in Patterns, instanceof, and switch (Preview) for additional information.
The following example uses instanceof as the type
comparison operator to test whether the Shape s is a
Circle. If it is, then it's safe to cast Shape s
to a Circle, which means that there's definitely a radius that you can
print:
public interface Shape { }
record Rectangle(double length, double width) implements Shape { }
record Circle(double radius) implements Shape { }
//...
Shape s = new Rectangle(4,3);
if (s instanceof Circle) {
Circle c = (Circle)s;
System.out.println("Radius: " + c.radius());
}The Java runtime throws a ClassCastException if it can't
cast a reference type to another reference type. If you remove the s instanceof
Circle expression from the example, the Java runtime throws an exception
when it tries to cast the Shape, which is a Rectangle,
to a Circle:
Shape s = new Rectangle(4,3);
// ClassCastException: class Rectangle cannot be cast
// to class Circle
Circle c = (Circle)s;
System.out.println("Radius: " + c.radius());When it comes to casting between primitive types, the Java runtime won't
ever throw a ClassCastException. It will convert the value (if the
types are compatible), which might result in the loss of information about the value's
overall magnitude as well as its precision and range. The following example casts an
int to a byte:
int i = 2345323;
byte b = (byte)i;
System.out.println(b);It prints the following output:
107An improperly or unintendedly converted value, such as from 2345323 to
107, or more subtly, from 2.05f to
2, can silently propagate through a program, causing potentially
elusive bugs.
As the primitive types int and byte differ
by their ranges, you could test that before casting the value:
if (i >= -128 && i <= 127) {
System.out.println((byte)i);
}However, a clearer and more concise way to test whether a cast between two
primitive type is safe is to use instanceof as the type comparison
operator:
if (i instanceof byte) {
System.out.println((byte)i);
}Note that this example still has to cast the value i to a
byte. The instanceof type comparison operator only
checks if i is a byte. It doesn't perform the actual
cast.
This also works with instanceof as the pattern match
operator, which has the added benefit of performing the cast for you if the test is
successful:
if (i instanceof byte b) {
System.out.println(b);
}Conversion Safety and switch
Consider the following example, which contains two switch
statements. The compiler generates an error for the first switch
statement:
float f = 100.01f;
switch (f) {
// error: the switch statement does not cover
// all possible input values
case int i ->
System.out.println(i + " as an int ");
}
switch (f) {
case double d ->
System.out.println(d + " as a double");
}The first switch statement isn't exhaustive because it only
processes int values. The case label case int i
converts the float value of the switch statement's
selector expression to an int value. This conversion is not safe, which
means that there's a possibility that information may be lost about the value's overall
magnitude as well as its precision and range. If this switch statement
were permitted, then an improperly or unintendedly converted value, such as from
100.01f to 100, can propagate through a program,
causing subtle bugs. To help prevent this from happening, the compiler generates an
error.
The second switch statement is exhaustive. The label
double d converts the selector expression's float
to a double. This is an unconditionally exact conversion, which
means that, at compile time, it's known that the conversion from the first type to the
second type is guaranteed not to lose information or throw an exception for any value.
Examples of unconditionally exact conversions include converting from
byte to int, from byte to
Byte, from int to long, and from
String to Object.
Whether a conversion between primitive types is unconditionally exact also
affects how patterns can dominate other patterns. (See Pattern Label Dominance.) In the following example, the compiler generates an error for the
type pattern byte b:
switch (f) {
case int i ->
System.out.println(i + " as an int");
// error: this case label is dominated by a preceding case label
case byte b ->
System.out.println(b + " as a byte");
default ->
System.out.println(f + " as a float");
}The type pattern int i dominates the type pattern
byte b because converting from byte to
int is unconditionally exact.