Fundamentals 15 min read

Understanding Polymorphism in Java: Concepts, Implementation, and Classic Examples

This article explains the three core object‑oriented concepts, focuses on runtime polymorphism in Java, describes its required conditions, demonstrates implementation through inheritance and interfaces with detailed code examples, and analyzes classic method‑overloading scenarios.

Java Captain
Java Captain
Java Captain
Understanding Polymorphism in Java: Concepts, Implementation, and Classic Examples

Polymorphism in Java

Object‑oriented programming (OOP) is built on three fundamental features: encapsulation, inheritance, and polymorphism. Encapsulation hides internal implementation, inheritance enables code reuse, and polymorphism allows a reference to invoke different method implementations at runtime.

Polymorphism means that the actual method executed is determined during program execution based on the concrete object type that a reference points to. This enables the same code to work with objects of different subclasses without modification.

Key Concepts

Upcasting (assigning a subclass object to a superclass reference) is required for polymorphic behavior.

When a superclass reference calls a method, the JVM selects the overridden method in the actual subclass.

Methods not declared in the superclass cannot be accessed through the superclass reference, even if they exist in the subclass.

Implementation Conditions in Java

To achieve runtime polymorphism in Java, three conditions must be met:

Inheritance relationship between classes.

Method overriding in the subclass.

Upcasting of the subclass instance to a superclass type.

Implementation via Inheritance

The following code demonstrates inheritance‑based polymorphism with a base class Wine and subclasses JNC and JGJ that override drink() and toString() :

public class Wine {
    private String name;
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String drink() { return "喝的是 " + getName(); }
    public String toString() { return null; }
}

public class JNC extends Wine {
    public JNC() { setName("JNC"); }
    @Override
    public String drink() { return "喝的是 " + getName(); }
    @Override
    public String toString() { return "Wine : " + getName(); }
}

public class JGJ extends Wine {
    public JGJ() { setName("JGJ"); }
    @Override
    public String drink() { return "喝的是 " + getName(); }
    @Override
    public String toString() { return "Wine : " + getName(); }
}

public class Test {
    public static void main(String[] args) {
        Wine[] wines = new Wine[2];
        wines[0] = new JNC();
        wines[1] = new JGJ();
        for (int i = 0; i < 2; i++) {
            System.out.println(wines[i].toString() + "--" + wines[i].drink());
        }
    }
}

Running this program prints the subclass implementations, confirming that the overridden methods are invoked.

Implementation via Interfaces

Polymorphism can also be achieved through interfaces, allowing a reference of the interface type to point to any implementing class. This provides greater flexibility because a class can implement multiple interfaces.

Classic Example – Method Overloading and Overriding

The article presents a classic example with classes A , B , C , and D to illustrate how the compiler selects methods based on the reference type and the actual object type:

public class A {
    public String show(D obj) { return "A and D"; }
    public String show(A obj) { return "A and A"; }
}
public class B extends A {
    public String show(B obj) { return "B and B"; }
    @Override
    public String show(A obj) { return "B and A"; }
}
public class C extends B {}
public class D extends B {}
public class Test {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();
        B b = new B();
        C c = new C();
        D d = new D();
        System.out.println("1--" + a1.show(b));
        System.out.println("2--" + a1.show(c));
        System.out.println("3--" + a1.show(d));
        System.out.println("4--" + a2.show(b));
        System.out.println("5--" + a2.show(c));
        System.out.println("6--" + a2.show(d));
        System.out.println("7--" + b.show(b));
        System.out.println("8--" + b.show(c));
        System.out.println("9--" + b.show(d));
    }
}

The output demonstrates how the JVM resolves method calls, emphasizing that only methods declared in the superclass can be invoked polymorphically, even when the subclass defines overloaded versions.

In summary, Java polymorphism relies on inheritance, method overriding, and upcasting, and can be realized through both class inheritance and interface implementation, enabling flexible and reusable code.

JavaooppolymorphismInheritancemethod overridingUpcasting
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.