Unit 3 · 16 min read
Inheritance, Polymorphism, Abstract Classes and Interfaces
What the syllabus asks for 7
- Inheritance
- Polymorphism and dynamic dispatch
- Abstract classes
- Interfaces
- The Object class and design guidance
- One call, many behaviours
- Eight uses of inheritance, polymorphism and interfaces
Inheritance and polymorphism are the features that let one piece of code work with many kinds of objects.
This unit covers extending classes, overriding, dynamic method dispatch, abstract classes, interfaces (including default methods and lambdas), and the design guidance that says when to use which.
extends,super, constructor chaining, overriding rules,@Override.- Dynamic dispatch: how Java chooses the method at run time (and what it does not do).
- Abstract classes versus interfaces; default, static and functional interfaces.
- The
Objectclass, casting andinstanceof; composition over inheritance.
3.1 Inheritance
A subclass extends one superclass and inherits its non-private members; Java has single inheritance of classes (but many interfaces). A subclass is-a superclass, so a Square can be used wherever a Rect is expected.
Constructors are not inherited: the first statement of a subclass constructor is super(...) (implicit super() if omitted), so the superclass part is built first.
Overriding redefines an inherited instance method with the same signature (same name and parameters; return type same or a subtype; access not narrower; no broader checked exceptions).
Mark it with @Override so the compiler catches typos. super.method() calls the superclass version. private, static and final methods cannot be overridden (static methods are hidden).
3.2 Polymorphism and dynamic dispatch
A superclass reference may hold a subclass object. When an overridden instance method is called through that reference, the JVM picks the version from the actual object's class at run time (dynamic dispatch, run-time polymorphism).
abstract class Shape {
abstract double area();
public String toString() { return getClass().getSimpleName() + String.format("[%.2f]", area()); }
}
class Circle extends Shape {
double r;
Circle(double r) { this.r = r; }
double area() { return Math.PI * r * r; }
}
class Rect extends Shape {
double w, h;
Rect(double w, double h) { this.w = w; this.h = h; }
double area() { return w * h; }
}
class Square extends Rect {
Square(double s) { super(s, s); }
}
interface Drawable {
void draw();
default String info() { return "drawable"; }
}
public class Shapes {
public static void main(String[] a) {
Shape[] list = { new Circle(1), new Rect(2, 3), new Square(4) };
double total = 0;
for (Shape s : list) { System.out.println(s); total += s.area(); }
System.out.printf("total=%.3f%n", total);
System.out.println(list[2] instanceof Rect);
Drawable d = () -> System.out.println("drawing");
d.draw();
System.out.println(d.info());
}
}
Circle[3.14]
Rect[6.00]
Square[16.00]
total=25.142
true
drawing
drawable
The loop calls s.area() once in the source, yet three different methods run. New shapes can be added (a Triangle) without changing this loop: that is the open–closed principle in action.
What is not polymorphic: fields and static methods are resolved by the declared (reference) type, not the object's class.
class Animal {
String name = "Animal";
String sound() { return "..."; }
static String kind() { return "animal"; }
}
class Dog extends Animal {
String name = "Dog";
@Override String sound() { return "Woof"; }
static String kind() { return "dog"; }
}
public class Dispatch {
public static void main(String[] x) {
Animal a = new Dog();
System.out.println(a.sound() + " " + a.name + " " + a.kind());
System.out.println(((Dog) a).name + " " + Dog.kind());
}
}
Woof Animal animal
Dog dog
Compile-time versus run-time: overloading is decided at compile time from the argument types; overriding is decided at run time from the object.
Casting: upcasting (Animal a = dog) is automatic and safe; downcasting (Dog) a needs a cast and throws ClassCastException if the object is not a Dog; check first with if (a instanceof Dog d) { … } (pattern matching, Java 16+).
One tool, many blades
A Swiss Army knife is one object with many tools behind one handle. Polymorphism is similar on the software side: one call, shape.area(), runs a different method depending on the object that receives it.
The reference type decides what you may call; the object type decides which version runs. Plate 3.1 computes the area of a circle, a rectangle and a triangle through the same call and adds them in one loop.

3.3 Abstract classes
An abstract class cannot be instantiated; it may have fields, constructors, concrete methods and abstract methods (no body) that subclasses must implement. Use it for a family sharing code and state: Shape above.
A class with any abstract method must itself be abstract; the first concrete subclass must implement them all.
3.4 Interfaces
An interface is a contract: a set of method signatures (implicitly public abstract) and constants (public static final). A class implements any number of interfaces, so interfaces give multiple inheritance of type. Since Java 8 an interface may also contain:
defaultmethods (with a body, inherited unless overridden),staticmethods, and (Java 9)privatemethods.
If a class inherits two defaults with the same signature it must override and choose (A.super.m()).
An interface with exactly one abstract method is a functional interface and can be implemented by a lambda (Runnable, Comparator<T>, Function<T,R>, Predicate<T>, Consumer<T>, Supplier<T>).
| Abstract class | Interface | |
|---|---|---|
| Instances | no | no |
| Fields | any | constants only |
| Constructors | yes | no |
| Methods | abstract + concrete | abstract + default/static |
| Inheritance | one class | many interfaces |
| Use when | related classes share state and code | unrelated classes share a capability (Comparable, Serializable, Runnable) |
import java.util.*;
public class Sorting {
static class Student implements Comparable<Student> {
String name; int marks;
Student(String n, int m) { name = n; marks = m; }
public int compareTo(Student o) { return Integer.compare(o.marks, marks); }
public String toString() { return name + "=" + marks; }
}
public static void main(String[] args) {
List<Student> l = new ArrayList<>(List.of(new Student("Sita", 82), new Student("Ram", 67), new Student("Gita", 91)));
Collections.sort(l);
System.out.println(l);
l.sort(Comparator.comparing(s -> s.name));
System.out.println(l);
}
}
[Gita=91, Sita=82, Ram=67]
[Gita=91, Ram=67, Sita=82]
Comparable<T> defines the natural order (compareTo: negative, zero, positive); Comparator<T> supplies an alternative order from outside.
One interface, many implementations
An interface is a promise: any class that implements it can be used wherever the interface is expected.
The TV-B-Gone is a small device that sends the switch-off codes of many makes of television in turn, so one button works with all of them; in code, one Comparator variable can hold many different ways of ordering the same list.
Plate 3.2 sorts four students by name and by marks using two Comparator implementations and gets two different, correct orders from the same sort call.

3.5 The Object class and design guidance
Object gives every class toString, equals, hashCode, getClass, clone, finalize (deprecated) and the thread methods wait/notify. final stops inheritance where it would break invariants; sealed classes (Java 17) restrict which classes may extend a type.
Inheritance vs composition. Inheritance is is-a (a Dog is an Animal); composition is has-a (a Car has an Engine).
Prefer composition and interfaces when you only need to reuse behaviour: inheritance couples the subclass tightly to the superclass's internals (the fragile base class problem).
Liskov's rule: a subclass must be usable wherever the superclass is without surprising the caller; a Square extending a mutable Rect breaks that, because setting the width of a square must also change its height.
Design principles (SOLID, in one line each): one reason to change per class; extend without modifying; subtypes substitutable; small focused interfaces; depend on abstractions, not concretions.
3.6 One call, many behaviours
Figure 3.1 shows a small payroll design that uses every idea of this unit. Payable is an interface: it promises a pay() method and says nothing about how. Employee is an abstract class that partly implements it and holds the shared field name. Teacher and Clerk are concrete subclasses with different pay rules, and Vendor is an unrelated class that also implements Payable.
A single loop over List<Payable> can print a pay slip for all of them, and at run time the JVM picks the right pay() for each object (dynamic dispatch).
The loop does not change when a new kind of payee is added, which is the practical value of polymorphism.
3.7 Eight uses of inheritance, polymorphism and interfaces
- Code reuse. A subclass inherits fields and methods of its parent and adds only what is new. A
Teacherand aClerksharenameandtoStringfromEmployee. Fixing a bug in the parent fixes it for every child. - Extending without editing. New behaviour is added by writing a new subclass, so tested code stays untouched. Adding a
Contractorto the payroll needs no change in the loop that prints pay slips. - Common handling of different objects. A parent-type or interface variable can hold any child. One method can accept a
Payableand work for staff, vendors and scholarship payments alike. - Contracts between teams. An interface fixes the method names and types before anyone writes the code. One team can write the user interface while another writes the payment code, and they meet at the interface.
- Multiple capabilities. A class can extend only one class but implement many interfaces. A
Studentcan beComparablefor sorting andSerializablefor saving at the same time. - Flexible testing. Code that depends on an interface can be tested with a simple fake object. A fee-collection program can be tested with a fake payment gateway instead of a real bank connection.
- Framework design. Libraries define interfaces such as
Runnable,ComparatorandList, and your classes plug in. The Android and Java collections frameworks both work this way. - Controlled templates. An abstract class gives the fixed steps and leaves some steps for subclasses. A report generator can fix the layout and let each subclass supply its own data.
Formula and fact sheet
- Single inheritance of classes, multiple of interfaces;
super(...)first in a constructor. - Override: same signature,
@Override, run-time dispatch. Overload: compile time. - Fields and static methods resolve by reference type.
- Abstract class: shared state and code; interface: capability contract; default methods allowed.
- Downcast needs
instanceofcheck. Prefer composition for reuse.
Chapter summary
- Inheritance reuses and specialises; constructors chain upward.
- Overridden methods are chosen at run time by the object's class, which is what lets one loop handle many shapes.
- Abstract classes share implementation among related types; interfaces define capabilities and enable lambdas.
- Good designs favour small interfaces and composition, keeping subclasses substitutable for their parents.
Practice
- Differentiate abstract class and interface. When would you choose each?
- Predict the output for a class
Awithvoid show(){print("A");},B extends Aoverriding it with "B", andA x = new B(); x.show();. - Create
Employee(abstract,salary()abstract) withFullTimeandPartTimesubclasses and print the total payroll of an array of mixed employees. - Why does
A a = new B(); a.fieldXread A's field even if B declares one with the same name? - What is wrong with calling an overridable method from a constructor?
- Write a functional interface
Operation { int apply(int a, int b); }and use lambdas for add and multiply. - Sort a list of strings by length, then alphabetically, using
Comparator. - What happens when a class implements two interfaces with the same default method?
- Explain
instanceofwith pattern matching and give an example of aClassCastException. - Explain with an example why
Square extends Rectanglecan violate Liskov substitution.
Answers and worked solutions
1. Abstract class: single inheritance, may hold state and constructors; use it for closely related classes sharing code. Interface: capability across unrelated classes, multiple implementation; use for contracts and callbacks.
2. Prints B (dynamic dispatch uses the object's class).
3. abstract class Employee { abstract double salary(); }; FullTime returns monthly pay; PartTime returns hours × rate; a loop adds e.salary() for each element of Employee[].
4. Fields are not overridden, only hidden; field access is resolved at compile time from the reference type A.
5. The superclass constructor runs before the subclass's fields are initialised, so the overridden method may see default values (0 or null) and fail.
6. Operation add = (a, b) -> a + b; Operation mul = (a, b) -> a * b; add.apply(2, 3) is 5 and mul.apply(2, 3) is 6.
7. list.sort(Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder()));
8. Compile error unless the class overrides the method; inside, it can pick one with A.super.m().
9. if (obj instanceof String s) { s.length(); } tests and binds. Object o = "text"; Integer i = (Integer) o; throws ClassCastException.
10. A Rectangle promises that setting width does not change height. A Square must change both, so code written for rectangles (r.setWidth(5); r.setHeight(2); assert area == 10) fails for a square.
Model answers: medium (5 marks) and long (10 marks) questions
Q1 (5 marks). Differentiate an abstract class and an interface, and show both in one Java program.
Introduction. Both define a type that cannot be instantiated directly and that subclasses must complete, but they serve different purposes: an abstract class shares state and partial code; an interface defines a pure capability.
Differences.
- State. An abstract class may have instance fields; an interface has only constants.
- Code. An abstract class may have constructors and ordinary methods; an interface has abstract methods plus
defaultandstaticmethods. - Inheritance. A class extends only one abstract class but may implement many interfaces.
- Relation. An abstract class expresses is-a with shared code; an interface expresses can-do.
| Point | Abstract class | Interface |
|---|---|---|
| Keyword | abstract class / extends |
interface / implements |
| Fields | any | public static final only |
| Constructor | yes | no |
| Multiple | no | yes |
import java.util.List;
public class PayDemo {
interface Payable {
double pay();
default String slip() { return String.format("Pay slip: Rs %.2f", pay()); }
}
static abstract class Employee implements Payable {
protected final String name;
Employee(String n) { name = n; }
@Override public String toString() { return name; }
}
static class Teacher extends Employee {
private final double hours, rate;
Teacher(String n, double h, double r) { super(n); hours = h; rate = r; }
public double pay() { return hours * rate; }
}
static class Clerk extends Employee {
Clerk(String n) { super(n); }
public double pay() { return 32000; }
}
static class Vendor implements Payable {
public double pay() { return 12500.5; }
@Override public String toString() { return "Vendor"; }
}
public static void main(String[] args) {
List<Payable> list = List.of(new Teacher("Sita", 90, 450), new Clerk("Ram"), new Vendor());
for (Payable p : list) System.out.println(p + " -> " + p.slip());
}
}
Sita -> Pay slip: Rs 40500.00
Ram -> Pay slip: Rs 32000.00
Vendor -> Pay slip: Rs 12500.50
Conclusion. Use an abstract class when subclasses share fields and code (Employee); use an interface for a capability that unrelated classes can offer (Payable, implemented even by Vendor).
Q2 (10 marks). Explain method overloading and overriding, dynamic dispatch and the instanceof check with a program. Differentiate overloading and overriding.
Introduction. Polymorphism means "many forms": one name, different behaviour. Java has compile-time polymorphism (overloading) and run-time polymorphism (overriding).
Concepts.
- Overloading. Several methods in the same class share a name but differ in parameter lists. The compiler chooses by the argument types.
- Overriding. A subclass supplies its own version of an inherited method with the same signature, marked
@Override. - Dynamic dispatch. For an overridden method the JVM picks the version from the actual object at run time, not from the declared type (Figure 3.1).
- Static methods. They are hidden, not overridden, so the declared type decides.
instanceof. Tests the real type before a downcast; a pattern such asa instanceof Dog dtests and casts in one step.super. Lets an override call the parent's version.final. Afinalmethod cannot be overridden; afinalclass cannot be extended.- Access. An override may not reduce visibility.
public class DispatchDemo {
static class Animal {
String sound() { return "..."; }
static String kind() { return "animal"; }
}
static class Dog extends Animal {
@Override String sound() { return "Woof"; }
static String kind() { return "dog"; }
String fetch() { return "fetching"; }
}
static int area(int side) { return side * side; }
static double area(double w, double h) { return w * h; }
public static void main(String[] args) {
Animal a = new Dog();
System.out.println("a.sound() = " + a.sound());
System.out.println("Animal.kind() = " + Animal.kind() + ", Dog.kind() = " + Dog.kind());
if (a instanceof Dog d) System.out.println("downcast ok: " + d.fetch());
System.out.println("area(3) = " + area(3) + ", area(2.5, 4) = " + area(2.5, 4));
Animal plain = new Animal();
System.out.println("plain instanceof Dog: " + (plain instanceof Dog));
}
}
a.sound() = Woof
Animal.kind() = animal, Dog.kind() = dog
downcast ok: fetching
area(3) = 9, area(2.5, 4) = 10.0
plain instanceof Dog: false
| Feature | Overloading | Overriding |
|---|---|---|
| Where | same class | parent and child |
| Signature | different parameters | identical |
| Binding | compile time | run time |
| Purpose | convenience | specialised behaviour |
Conclusion. The variable's declared type decides what can be called, while the object's real type decides which overridden version runs. That separation makes extensible designs possible.