Fundamentals 7 min read

Java Type Checking, Polymorphism, Upcasting and Downcasting

This article explains Java's strong typing, type declarations, basic primitive conversions (narrowing and widening), and demonstrates upcasting, downcasting, and polymorphism through concrete class examples such as Human, Cup, and BrokenCup, highlighting how the runtime resolves method calls.

Java Captain
Java Captain
Java Captain
Java Type Checking, Polymorphism, Upcasting and Downcasting

Java requires every variable and reference to have a type declaration before it can be used; the language performs strong type checking and will report errors when a type mismatch occurs.

public class Test { public static void main(String[] args) { Human aPerson; aPerson = new Cup(); // compile‑time error: required: Human, found: Cup } } class Human { private int height; public Human(int h) { this.height = h; } public int getHeight() { return this.height; } public void growHeight(int h) { this.height += h; } } class Cup { private int water = 0; public void addWater(int w) { this.water += w; } public void drinkWater(int w) { this.water -= w; } }

Java also supports primitive type conversions. A narrowing conversion (e.g., int a = (int)1.23; ) may lose information and must be explicit, while a widening conversion (e.g., assigning an int to a double ) is performed automatically.

public class Test { public static void main(String[] args) { int a; a = (int)1.23; // narrowing System.out.println(a); int b = 3; double d = b; // widening System.out.println(d); } }

Upcasting allows a subclass reference to be treated as its superclass type, enabling polymorphic method calls. In the following example, a BrokenCup (subclass of Cup ) is upcast to Cup , but the overridden addWater method of BrokenCup is invoked at runtime.

public class Test { public static void main(String[] args) { Cup aCup; BrokenCup aBrokenCup = new BrokenCup(); aCup = aBrokenCup; // upcast aCup.addWater(10); // calls BrokenCup.addWater } } class Cup { public void addWater(int w) { /* ... */ } public void drinkWater(int w) { /* ... */ } } class BrokenCup extends Cup { @Override public void addWater(int w) { System.out.println("shit, broken cup"); } @Override public void drinkWater(int w) { System.out.println("om...num..., no water inside"); } }

Polymorphism is the mechanism by which Java determines the actual object type at runtime and dispatches the appropriate overridden method, even when the reference type is a superclass.

Downcasting converts a superclass reference back to a subclass reference, but only when the object truly is an instance of that subclass; otherwise a ClassCastException occurs.

All classes in Java inherit from the root Object class, which provides common methods such as toString() . Overriding these methods allows custom behavior for any object.

In summary, the article covered basic type checking, primitive narrowing and widening conversions, upcasting with polymorphic method binding, downcasting requirements, and the universal Object superclass.

Javatype checkingpolymorphismInheritancedowncastingUpcasting
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.