12.4 Inheritance, Polymorphism, and Overloading vs. Overriding

Key Takeaways

  • Inheritance models an "is-a" relationship: in ETS notation, class Dog extends Animal gives Dog all of Animal's public methods, which Dog can add to or override.
  • Benefits of inheritance include reusing code, organizing classes into hierarchies, extending a program by adding subclasses, and treating related objects uniformly through a superclass type.
  • Overriding: a subclass defines a method with the same name and parameter list as a superclass method, and the object's actual class determines which version runs.
  • Overloading: one class defines several methods with the same name but different numbers or types of parameters, and the arguments in a call determine which version runs.
  • Use inheritance for "is-a" relationships and composition (an object holding another as an instance variable) for "has-a" relationships.
Last updated: September 2026

What this competency asks

Under object-oriented programming concepts, ETS asks you to:

  • Identify the benefits of inheritance (and encapsulation, Section 12.3).
  • Identify distinctions between overloading and overriding.

The discussion question asks for an example of each and an explanation of the difference.

Inheritance

Inheritance lets a new class (the subclass, or child) be defined as an extension of an existing class (the superclass, or parent). In ETS notation the keyword is extends.

class Animal
    private String name
    public String getName ( )
        return name
    end getName
    public String speak ( )
        return "..."
    end speak
end class Animal

class Dog extends Animal
    public String speak ( )          // overrides Animal's speak
        return "Woof"
    end speak
    public void fetch ( )            // new behavior only dogs have
        print "Fetching!"
    end fetch
end class Dog

A Dog inherits getName without rewriting it, overrides speak, and adds fetch. Private superclass fields such as name are part of every Dog, but Dog's own code reaches them only through inherited public (or protected) methods.

"Is-a" versus "has-a"

RelationshipTestDesign
Is-a"A Dog is an Animal." TrueInheritance: Dog extends Animal
Has-a"A Car has an Engine." True; "a Car is an Engine" is falseComposition: Car has an instance variable of type Engine

Using inheritance for a has-a relationship, such as making Car extends Engine, is a design error. It gives cars every engine method and misrepresents the model.

Benefits of inheritance

BenefitExample
Code reuseShared fields and methods such as getName are written once, in the superclass
OrganizationHierarchies mirror the domain: Shape → Circle, Rectangle
ExtensibilityAdd a Triangle subclass without changing existing classes (Section 10.4)
PolymorphismCode written for Shape works for any subclass
ConsistencyA fix in the superclass applies to every subclass

Constructors and super

When a subclass object is created, the superclass part is initialized first, then the subclass's own part. In Java, a subclass constructor calls super ( … ) as its first statement to run the superclass constructor. The keyword super also lets an overriding method call the superclass version, as in super.speak ( ).

Overriding and polymorphism

Overriding means a subclass provides its own version of an inherited method with the same name and the same parameter list. Which version runs is decided by the object's actual class when the program runs. This is dynamic dispatch, the core of polymorphism.

Animal pet ← new Dog ( )
print pet.speak ( )           // prints Woof

The variable's declared type is Animal, but the object is a Dog, so Dog's speak runs. A loop over a list of Animal references prints each animal's own sound. Adding a new subclass needs no change to the loop.

Polymorphism in a loop

class Shape
    public double area ( )
        return 0.0
    end area
end class Shape

class Rectangle extends Shape
    private double width
    private double height
    public double area ( )
        return width * height
    end area
end class Rectangle

class Circle extends Shape
    private double radius
    public double area ( )
        return 3.14 * radius * radius
    end area
end class Circle

// constructors not shown: a 2 × 5 rectangle and a circle of radius 1
Shape[ ] shapes ← { new Rectangle ( 2, 5 ), new Circle ( 1 ) }
double total ← 0
for ( int i ← 0; i < 2; i ← i + 1 )
    total ← total + shapes[i].area ( )
end for
print total

Trace: shapes[0] is a Rectangle, so its area returns 2 × 5 = 10. shapes[1] is a Circle, so its area returns 3.14 × 1 × 1 = 3.14. The program prints 13.14. The loop never asks which kind of shape it holds. Each object answers with its own override, which is why a new Triangle extends Shape class can join the array without any change to the loop.

Abstract classes and interfaces

The area method in Shape returns a meaningless 0.0 only so that every shape has one. In Java, Shape would normally be declared abstract, with public abstract double area(); written without a body. An abstract class cannot be instantiated with new, and every concrete subclass must override its abstract methods. An interface goes one step further: it lists method headers that any implementing class promises to supply (modern Java interfaces may also include default methods with bodies). A Java class can extend only one class (single inheritance) but can implement many interfaces. Python and C++ allow a class to have several superclasses. ETS's pseudocode keyword list contains only extends, new, public, and private, so treat abstract and interface as Java vocabulary to recognize.

Overloading

Overloading means one class has several methods with the same name but different parameter lists (a different number, type, or order of parameters). The compiler chooses the version whose parameters match the call's arguments.

class Geometry
    public int area ( int side )                  // square
        return side * side
    end area
    public int area ( int width, int height )     // rectangle
        return width * height
    end area
end class Geometry

area ( 5 ) returns 25 and area ( 3, 4 ) returns 12. A different return type alone does not make a valid overload. The parameter lists must differ.

Overloading vs. overriding

OverloadingOverriding
WhereUsually within one classSubclass redefines a superclass method
NameSameSame
Parameter listMust differMust be the same
Chosen byThe arguments in the call (decided at compile time)The object's actual class (decided at run time)
PurposeConvenience: one name for related operations on different inputsSpecialization: a subclass behaves in its own way
Examplearea ( int ) and area ( int, int )Dog.speak ( ) replaces Animal.speak ( )

Worked example

class Vehicle {
    public void start() { System.out.print("V_start "); }
}
class Car extends Vehicle {
    public void start() { System.out.print("C_start "); }            // overrides
    public void start(int keyId) { System.out.print("Key_" + keyId); } // overloads
}

Vehicle v = new Car();
v.start();

The output is C_start . v refers to a Car, and Car overrides the no-argument start, so dynamic dispatch picks Car's version. The start(int) method is an overload: same name, different parameters. It is not called here.

Traps that separate the two

  • The declared type limits which methods can be called. After Animal pet ← new Dog ( ), the call pet.fetch ( ) does not compile in Java, because the compiler checks the declared type Animal, and Animal has no fetch. The declared type decides which methods may be called. The actual object decides which override runs.
  • A changed parameter list turns an intended override into an overload. A Java Dog class that defines public boolean equals ( Dog other ) has not overridden the equals ( Object o ) method every class inherits from Object. It has overloaded it. Library code such as list.contains ( someDog ) calls equals with an Object parameter, so the new method is silently skipped. Writing @Override above a method asks the Java compiler to confirm that it really overrides something, which turns this mistake into a compile-time error.
  • Constructors are neither inherited nor overridden. Each class writes its own, and a subclass constructor reaches the superclass constructor through super ( … ).
  • Python has no signature-based overloading. A second def area ( self, w, h ) in the same Python class simply replaces the first definition. Python code gets the same convenience from default parameter values, such as def area ( self, w, h = None ). Overriding works in Python as it does in Java.
Test Your Knowledge

In Java, what is printed?

class Vehicle {
    public void start() { System.out.print("V_start "); }
}
class Car extends Vehicle {
    public void start() { System.out.print("C_start "); }
    public void start(int keyId) { System.out.print("Key_" + keyId + " "); }
}
Vehicle v = new Car();
v.start();

A
B
C
D
Test Your Knowledge

A class contains double price ( int quantity ) and double price ( int quantity, double discount ). Which term describes these two methods?

A
B
C
D
Test Your Knowledge

A student designs class Car extends Engine so that cars can use engine methods. What is the best critique of this design?

A
B
C
D
Test Your Knowledge

When a new object of a subclass is created, in what order do the constructors run?

A
B
C
D
Test Your Knowledge

In Java, class Dog defines public boolean equals(Dog other) to compare two dogs' names. A programmer expects list.contains(someDog) to use this method, but it never runs. What best explains why?

A
B
C
D