Upcasting and downcasting
Upcasting means treating a subclass object as its superclass type (moving up the hierarchy). Downcasting means treating a superclass reference as a subclass type again (moving down). Casting never changes the object itself; it only changes the type through which you look at it.
Shape s = new Circle(2); // upcasting: automatic and safe
if (s instanceof Circle) {
Circle c = (Circle) s; // downcasting: explicit; check first
System.out.println(c.r);
}Through a Shape reference you can only call methods declared in Shape (though the overriding version runs). Downcast to reach subclass-only members.
Careful: a downcast is checked at run time. Shape s = new Rectangle(4, 5); Circle c = (Circle) s; compiles, but throws a ClassCastException because the object is not a Circle. Always test with instanceof first.
Why upcasting is always safe
Every Circle is a Shape, so anything you can ask a Shape to do, a Circle can also do. The compiler allows upcasting automatically, with no cast written. It happens all the time without you noticing: when you put a Circle in a Shape[] array, or pass a Rectangle to a method that takes a Shape parameter.
Why downcasting needs care
Not every Shape is a Circle. When you write (Circle) s, you are telling the compiler "trust me, this object is a Circle". The compiler accepts this, but the JVM checks it when the line runs. If you were wrong, it throws a ClassCastException. That is why you test first with instanceof, which returns true when the object is an instance of that class or of any of its subclasses:
Shape s = new Circle(2);
System.out.println(s instanceof Circle); // true
System.out.println(s instanceof Shape); // true: a Circle is a Shape
System.out.println(s instanceof Rectangle); // falsenull instanceof Circle is always false, so an instanceof test also protects you from a NullPointerException.
Pattern matching for instanceof (Java 16+)
Modern Java can test and cast in one step:
if (s instanceof Circle c) { // if s is a Circle, call it c
System.out.println(c.r); // c is already a Circle here
}This is shorter and removes the risk of casting to the wrong type by mistake.
When should you downcast?
Rarely. If you find yourself writing many instanceof tests followed by casts, it is usually a sign that the behaviour belongs in an overridden method instead. For example, rather than asking "is this a Circle? then print its radius", give Shape a describe() method that each subclass overrides. Downcasting is reasonable when you really do need a feature that only one subclass has.
Remember
- Upcasting (subclass to superclass) is automatic and always safe.
- Downcasting needs an explicit cast and is checked at run time; test with
instanceoffirst. - Many casts usually mean polymorphism could be used instead.