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.
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"
| Relationship | Test | Design |
|---|---|---|
| Is-a | "A Dog is an Animal." True | Inheritance: Dog extends Animal |
| Has-a | "A Car has an Engine." True; "a Car is an Engine" is false | Composition: 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
| Benefit | Example |
|---|---|
| Code reuse | Shared fields and methods such as getName are written once, in the superclass |
| Organization | Hierarchies mirror the domain: Shape → Circle, Rectangle |
| Extensibility | Add a Triangle subclass without changing existing classes (Section 10.4) |
| Polymorphism | Code written for Shape works for any subclass |
| Consistency | A 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
| Overloading | Overriding | |
|---|---|---|
| Where | Usually within one class | Subclass redefines a superclass method |
| Name | Same | Same |
| Parameter list | Must differ | Must be the same |
| Chosen by | The arguments in the call (decided at compile time) | The object's actual class (decided at run time) |
| Purpose | Convenience: one name for related operations on different inputs | Specialization: a subclass behaves in its own way |
| Example | area ( 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 callpet.fetch ( )does not compile in Java, because the compiler checks the declared typeAnimal, andAnimalhas nofetch. 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
Dogclass that definespublic boolean equals ( Dog other )has not overridden theequals ( Object o )method every class inherits fromObject. It has overloaded it. Library code such aslist.contains ( someDog )callsequalswith anObjectparameter, so the new method is silently skipped. Writing@Overrideabove 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 asdef area ( self, w, h = None ). Overriding works in Python as it does in Java.
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 class contains double price ( int quantity ) and double price ( int quantity, double discount ). Which term describes these two methods?
A student designs class Car extends Engine so that cars can use engine methods. What is the best critique of this design?
When a new object of a subclass is created, in what order do the constructors run?
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?