Unit 3 · OOP Concepts

3.6.3Upcasting, downcasting and instanceof

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.

Java
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);
}
Shapearea()Circler: doublearea()upcastautomatic and safeShape s = new Circle(2);downcastexplicit: check firstif (s instanceof Circle)(Circle) s
Upcasting is automatic and safe; downcasting needs an explicit cast and an instanceof check

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:

Java
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);  // false

null 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:

Java
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 instanceof first.
  • Many casts usually mean polymorphism could be used instead.