thecodex.expert · The Codex Family of Knowledge
Java

Inheritance and Polymorphism

A variable declared as the parent type, but holding a child object, calls the CHILD’s overridden method — the reference’s declared type has nothing to do with which version actually runs.

Java 21 LTS Resolved by runtime type Last verified:
Canonical Definition

extends establishes single inheritance: a subclass automatically has every non-private field and method of its superclass, plus whatever it adds or overrides itself. Polymorphism is the resulting behavior at a method call site: when a subclass overrides a method, calling that method through a superclass-typed reference still invokes the subclass's version, because Java resolves overridden methods based on the object's actual runtime type — dynamic dispatch — not the declared type of the variable holding the reference.

extends: single inheritance

Dog automatically has eat() without redeclaring it, and provides its own version of makeSound() — super() explicitly calls the parent constructor, required as the first statement whenever the parent has no no-argument constructor.

JavaAnimal.java
public class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }

    public void eat() { System.out.println(name + " is eating"); }
    public void makeSound() { System.out.println("..."); }
}

public class Dog extends Animal {
    public Dog(String name) {
        super(name);   // must call the parent's constructor explicitly
    }

    @Override
    public void makeSound() {   // overrides the parent's version
        System.out.println(name + " says Woof!");
    }
}

Polymorphism: resolved by the ACTUAL object, not the reference type

Even though pet is declared as Animal, calling makeSound() runs Dog's version — the JVM looks at what pet actually points to at runtime, ignoring the declared reference type entirely for this decision.

JavaPolymorphism.java
Animal pet = new Dog("Rex");   // declared type: Animal, actual type: Dog
pet.makeSound();                 // prints "Rex says Woof!" — Dog's override runs

Animal[] animals = { new Dog("Rex"), new Cat("Whiskers") };
for (Animal a : animals) {
    a.makeSound();   // each one calls ITS OWN override, through the same Animal-typed variable
}

super: accessing the parent's version explicitly

super.makeSound() inside the override calls the parent's original implementation, letting a subclass extend rather than completely replace inherited behavior.

JavaSuperCall.java
public class Puppy extends Dog {
    @Override
    public void makeSound() {
        super.makeSound();   // calls Dog's makeSound() first
        System.out.println("(but it's more of a yip)");
    }
}

Sources

1
Oracle. The Java Tutorials, "Overriding and Hiding Methods" and "Polymorphism," docs.oracle.com/javase/tutorial/java/IandI.