Casting, instanceof & pattern matching
Upcasting is always safe; downcasting needs instanceof or a ClassCastException at runtime.
Moving up and down the hierarchy
Upcasting — assigning a subclass reference to a superclass type — is implicit and always safe:
Animal a = new Dog("Rex", "Lab"); // no cast needed
Downcasting — assigning a superclass reference to a subclass type — requires an explicit cast and can throw ClassCastException at runtime if the object is not actually of the target type:
Animal a = new Dog("Rex", "Lab");
Dog d = (Dog) a; // safe: a actually holds a Dog
Animal a2 = new Cat("Whiskers");
Dog d2 = (Dog) a2; // compiles, but throws ClassCastException at runtime
instanceof — guard before casting:
if (a instanceof Dog) {
Dog d = (Dog) a;
d.fetch();
}
Java 16+ pattern matching collapses the guard and cast into one:
if (a instanceof Dog d) { // d is in scope only inside the if-body
d.fetch(); // no explicit cast needed
}
The pattern variable d is created only when the check passes. This removes an entire class of ClassCastException bugs.