The code below succeeds in Java 8 but throws a ClassCastException in Java 11. Why did the behavior change?
I could not find any related changes in OpenJDK's Java 9, Java 10 or Java 11 feature sets.
public class GenericsExample {
public static void main(String[] args) {
Set<Car> set = new HashSet<>();
set.add(getAnimal());
}
static <T extends Animal> T getAnimal() {
return (T) new Animal() {};
}
interface Animal {}
class Car {}
}
ClassCastException is a runtime exception raised in Java when we try to improperly cast a class from one type to another. It's thrown to indicate that the code has attempted to cast an object to a related class, but of which it is not an instance.
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.
If we write code like this: String obj = (String) hmp. get(key); it would throw a class cast exception, because the value returned by the get method of the hash map would be an Array list, but we are trying to cast it to a String.
ClassCast Exception is thrown when we try to cast an object of the parent class to the child class object. However, it can also be thrown when we try to convert the objects of two individual classes that don't have any relationship between them.
Indeed it was a bug in Java 8, which was fixed in Java 9 - bugfix.
In some scenarios, the javac CHECKCAST
instruction was skipped.
If you are curious, consider those 2 lines of code:
Set<Car> set = new HashSet<>(); // line 11
set.add(getAnimal()); // line 12
Java 8 bytecode will look like this:
LINENUMBER 11 L1
ALOAD 1
ALOAD 0
INVOKEVIRTUAL UserManagerTest.getAnimal ()LUserManagerTest$Animal;
INVOKEINTERFACE java/util/Set.add (Ljava/lang/Object;)Z (itf)
POP
But Java 9 will look like this:
LINENUMBER 11 L1
ALOAD 1
ALOAD 0
INVOKEVIRTUAL UserManagerTest.getAnimal ()LUserManagerTest$Animal;
CHECKCAST UserManagerTest$Car
INVOKEINTERFACE java/util/Set.add (Ljava/lang/Object;)Z (itf)
POP
The only difference is CHECKCAST
instruction, which (according to JavaDoc) states that the named class, array, or interface type is resolved. If object can be cast to the resolved class, array, or interface type, the operand stack is unchanged; otherwise, the checkcast instruction throws a ClassCastException.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With