Home
4Inheritance, Polymorphism & Overriding
4

Lab Manual IV · CSE 202

Inheritance, Polymorphism & Overriding

Build a Vehicle hierarchy with Car, Motorcycle and ElectricCar: reuse code with extends and super, override display(), and let one loop drive every kind of vehicle.

← → move between slides · F full screen · M slide list

Before we start

Why this lab

A car, a motorcycle and an electric car all accelerate and brake the same way, but each shows different details. Do we write accelerate() three times?

No: write it once in Vehicle and inherit it. Each subclass then overrides only what differs, and one Vehicle[] array can hold them all.

Lab 4 of 10

Lab at a glance

After this lab you can

  • Explain inheritance as code reuse and an is-a relationship
  • Create subclasses with extends
  • Call superclass constructors with super(…)
  • Override methods with @Override
  • Distinguish overriding from overloading
  • Use superclass references to subclass objects (polymorphism)
  • Cast safely with instanceof

Before you start

  • Labs 2–3: classes, constructors, encapsulation, constructor chaining, overloading

Plan for the session

  1. Theory recap
  2. Walk through the program, section by section, and run it
  3. In-lab tasks, shown to the instructor
  4. Report exercises at home

Theory 1 of 5

Inheritance and extends

  • class Car extends Vehicle: a Car is a Vehicle and inherits its accessible fields and methods.
  • Java classes have single inheritance, but chains are allowed: ElectricCar → Car → Vehicle.

Theory 2 of 5

super and protected

public Car(String make, int year, int numDoors, double fuelLevel) {
    super(make, year);          // Vehicle part first
    this.numDoors = numDoors;
    ...
}
  • super(…) runs the superclass constructor and must be the first statement.
  • protected fields (make, year, speed) are visible to subclasses; subclass-only fields stay private.

Theory 3 of 5

Overriding vs overloading

OverloadingOverriding
Wheresame classsubclass
Signaturedifferent parameterssame name, parameters, return type
Chosenat compile timeat run time (object type)
Exampledisplay() / display(String)Car.display() replaces Vehicle.display()
  • @Override makes the compiler check that you really override something.

Theory 4 of 5

Polymorphism: one call, many behaviours

Vehicle[] vehicles = {vehicle, car, motorcycle, electricCar};
for (Vehicle v : vehicles) {
    v.display();   // Car, Motorcycle or ElectricCar version
}
  • The reference type (Vehicle) decides what you may call; the object type decides which version runs.
  • This is dynamic method dispatch.

Theory 5 of 5

instanceof and safe casting

Vehicle v = new Car("Honda Civic", 2021, 2, 60.0);
if (v instanceof Car) {
    Car c = (Car) v;
    System.out.println(c.getNumDoors());
}
  • A Vehicle reference cannot call getNumDoors(); cast it first.
  • Check with instanceof before casting to avoid a ClassCastException.

Implementation design

Execution flow

  1. Create a Vehicle, a Car, a Motorcycle and an ElectricCar
  2. Call display() on each directly
  3. Store them in a Vehicle[] and display them polymorphically
  4. Call inherited accelerate() and brake()
  5. Call subclass-only refuel(), wheelie() and charge()
  6. Test instanceof and cast a Vehicle reference to Car
  7. Verify overriding through Vehicle references
  8. Show brake() never makes speed negative

Common state in Vehicle; each subclass adds fields and overrides display().

Code walkthrough 1 of 10 · Vehicle.java

The superclass

// Protected fields are accessible in subclasses
protected String make;
protected int year;
protected int speed;

public Vehicle(String make, int year) {
    this.make = make;
    this.year = year;
    this.speed = 0;
}
  • protected fields are visible to subclasses.

Code walkthrough 2 of 10 · Vehicle.java

Methods every vehicle inherits

public void accelerate(int delta) {
    if (delta > 0) {
        this.speed = this.speed + delta;
    }
}

public void brake(int delta) {
    if (delta > 0) {
        this.speed = Math.max(0, this.speed - delta);
    }
}

Code walkthrough 2 of 10 · Vehicle.java

Methods every vehicle inherits (cont.)

public void display() {
    System.out.println("------- Vehicle -------");
    System.out.println("Make  : " + make);
    System.out.println("Year  : " + year);
    System.out.println("Speed : " + speed + " km/h");
    System.out.println("-----------------------");
}
  • Car, Motorcycle and ElectricCar get these without writing them again.

Code walkthrough 3 of 10 · Car.java

Car extends Vehicle

public class Car extends Vehicle {

    private int numDoors;
    private double fuelLevel;

    public Car(String make, int year, int numDoors, double fuelLevel) {
        super(make, year);
        if (numDoors > 0) {
            this.numDoors = numDoors;
        } else {
            this.numDoors = 4;

Code walkthrough 3 of 10 · Car.java

Car extends Vehicle (cont.)

        }
        if (fuelLevel >= 0.0 && fuelLevel <= 100.0) {
            this.fuelLevel = fuelLevel;
        } else {
            this.fuelLevel = 0.0;
        }
    }
  • super(make, year) builds the Vehicle part first.
  • Then Car validates its own fields.

Code walkthrough 4 of 10 · Car.java

Overriding display()

@Override
public void display() {
    System.out.println("------- Car -------");
    System.out.println("Make      : " + make);
    System.out.println("Year      : " + year);
    System.out.println("Speed     : " + speed + " km/h");
    System.out.println("Doors     : " + numDoors);
    System.out.printf("Fuel Level: %.1f%%%n", fuelLevel);
    System.out.println("-------------------");
}
  • Same signature as Vehicle.display(); @Override lets the compiler check it.
  • make, year and speed are inherited protected fields.

Code walkthrough 5 of 10 · Motorcycle.java

Motorcycle: another subclass

public class Motorcycle extends Vehicle {

    private boolean hasStorage;

    public Motorcycle(String make, int year, boolean hasStorage) {
        super(make, year);
        this.hasStorage = hasStorage;
    }

    public void wheelie() {
        System.out.println(make + " is performing a wheelie!");
    }
  • Adds its own field and a method only motorcycles have.
  • Its display() override follows the same pattern as Car’s.

Code walkthrough 6 of 10 · ElectricCar.java

ElectricCar: a chain of inheritance

public class ElectricCar extends Car {

    private double batteryCapacity;
    private double chargeLevel;

    public ElectricCar(String make, int year, int numDoors,
                       double batteryCapacity, double chargeLevel) {
        super(make, year, numDoors, 100.0);
        if (batteryCapacity > 0.0) {
            this.batteryCapacity = batteryCapacity;
        } else {

Code walkthrough 6 of 10 · ElectricCar.java

ElectricCar: a chain of inheritance (cont.)

            this.batteryCapacity = 50.0;
        }
        if (chargeLevel >= 0.0 && chargeLevel <= 100.0) {
            this.chargeLevel = chargeLevel;
        } else {
            this.chargeLevel = 0.0;
        }
    }
  • ElectricCar → Car → Vehicle. super(…) calls Car’s constructor.

Code walkthrough 7 of 10 · Lab4Demo.java

Polymorphic dispatch

// 3. Polymorphism with Superclass References
System.out.println("\n=== Polymorphic Dispatch ===");
Vehicle[] vehicles = {vehicle, car, motorcycle, electricCar};
for (int i = 0; i < vehicles.length; i++) {
    System.out.println("vehicles[" + i + "]:");
    displayVehicle(vehicles[i]);
}
  • One Vehicle[] holds every kind of vehicle.
  • Each element runs its own display().

Code walkthrough 8 of 10 · Lab4Demo.java

Inherited and subclass-only methods

// 5. Subclass-Specific Methods
System.out.println("\n=== Subclass-Specific Methods ===");
car.refuel(20.0);
System.out.println("After car.refuel(20.0):");
car.display();
motorcycle.wheelie();
electricCar.charge(15.0);
System.out.println("After electricCar.charge(15.0):");
electricCar.display();
  • refuel, wheelie and charge exist only in their subclasses.

Code walkthrough 9 of 10 · Lab4Demo.java

instanceof and a safe cast

// 6. instanceof and Safe Casting
System.out.println("\n=== instanceof and Safe Casting ===");
Vehicle v = new Car("Honda Civic", 2021, 2, 60.0);
System.out.println("v instanceof Vehicle    : " + (v instanceof Vehicle));
System.out.println("v instanceof Car        : " + (v instanceof Car));
System.out.println("v instanceof Motorcycle : " + (v instanceof Motorcycle));
if (v instanceof Car) {
    Car castedCar = (Car) v;
    System.out.println("Number of doors after casting: " + castedCar.getNumDoors());
}
  • Check the real type, then cast to reach Car-only methods.

Code walkthrough 10 of 10 · Lab4Demo.java

Overriding through Vehicle references

// 7. Method Overriding Verification
System.out.println("\n=== Method Overriding Verification ===");
Vehicle ref1 = new Car("Ford Mustang", 2020, 2, 50.0);
Vehicle ref2 = new Motorcycle("Harley-Davidson", 2022, false);
Vehicle ref3 = new ElectricCar("Nissan Leaf", 2023, 4, 62.0, 70.0);
ref1.display();
ref2.display();
ref3.display();
  • The reference type is Vehicle, but each object’s own display() runs.

Step through the code · 1 of 2

Trace: super(…) and dynamic dispatch

Watch super(…) build the inherited part, then one call site run two different display() methods.

Step through the code · 1 of 2

Trace: super(…) and dynamic dispatch (cont.)

Step through the code · 2 of 2

Trace: instanceof before every cast

One loop over a Vehicle[]: the check decides whether the cast is safe. The last line shows what happens without it.

Step through the code · 2 of 2

Trace: instanceof before every cast (cont.)

For reference · 5 files

The complete program

public class Vehicle {

    // Protected fields are accessible in subclasses
    protected String make;
    protected int year;
    protected int speed;

    public Vehicle(String make, int year) {
        this.make = make;
        this.year = year;
        this.speed = 0;
    }

    public void accelerate(int delta) {
        if (delta > 0) {
            this.speed = this.speed + delta;
        }
    }

    public void brake(int delta) {
        if (delta > 0) {
            this.speed = Math.max(0, this.speed - delta);
        }
    }

    public void display() {
        System.out.println("------- Vehicle -------");
        System.out.println("Make  : " + make);
        System.out.println("Year  : " + year);
        System.out.println("Speed : " + speed + " km/h");
        System.out.println("-----------------------");
    }

    public int getYear() {
        return year;
    }
}
public class Car extends Vehicle {

    private int numDoors;
    private double fuelLevel;

    public Car(String make, int year, int numDoors, double fuelLevel) {
        super(make, year);
        if (numDoors > 0) {
            this.numDoors = numDoors;
        } else {
            this.numDoors = 4;
        }
        if (fuelLevel >= 0.0 && fuelLevel <= 100.0) {
            this.fuelLevel = fuelLevel;
        } else {
            this.fuelLevel = 0.0;
        }
    }

    public void refuel(double amount) {
        if (amount > 0.0) {
            this.fuelLevel = Math.min(100.0, this.fuelLevel + amount);
        }
    }

    @Override
    public void display() {
        System.out.println("------- Car -------");
        System.out.println("Make      : " + make);
        System.out.println("Year      : " + year);
        System.out.println("Speed     : " + speed + " km/h");
        System.out.println("Doors     : " + numDoors);
        System.out.printf("Fuel Level: %.1f%%%n", fuelLevel);
        System.out.println("-------------------");
    }

    public int getNumDoors() { return numDoors; }
    public double getFuelLevel() { return fuelLevel; }
}
public class Motorcycle extends Vehicle {

    private boolean hasStorage;

    public Motorcycle(String make, int year, boolean hasStorage) {
        super(make, year);
        this.hasStorage = hasStorage;
    }

    public void wheelie() {
        System.out.println(make + " is performing a wheelie!");
    }

    @Override
    public void display() {
        System.out.println("------- Motorcycle -------");
        System.out.println("Make        : " + make);
        System.out.println("Year        : " + year);
        System.out.println("Speed       : " + speed + " km/h");
        System.out.println("Has Storage : " + (hasStorage ? "Yes" : "No"));
        System.out.println("--------------------------");
    }

    public boolean hasStorage() { return hasStorage; }
}
public class ElectricCar extends Car {

    private double batteryCapacity;
    private double chargeLevel;

    public ElectricCar(String make, int year, int numDoors,
                       double batteryCapacity, double chargeLevel) {
        super(make, year, numDoors, 100.0);
        if (batteryCapacity > 0.0) {
            this.batteryCapacity = batteryCapacity;
        } else {
            this.batteryCapacity = 50.0;
        }
        if (chargeLevel >= 0.0 && chargeLevel <= 100.0) {
            this.chargeLevel = chargeLevel;
        } else {
            this.chargeLevel = 0.0;
        }
    }

    public void charge(double amount) {
        if (amount > 0.0) {
            this.chargeLevel = Math.min(100.0, this.chargeLevel + amount);
        }
    }

    @Override
    public void display() {
        System.out.println("------- Electric Car -------");
        System.out.println("Make             : " + make);
        System.out.println("Year             : " + year);
        System.out.println("Speed            : " + speed + " km/h");
        System.out.println("Doors            : " + getNumDoors());
        System.out.printf("Battery Capacity : %.1f kWh%n", batteryCapacity);
        System.out.printf("Charge Level     : %.1f%%%n", chargeLevel);
        System.out.println("----------------------------");
    }

    public double getBatteryCapacity() { return batteryCapacity; }
    public double getChargeLevel() { return chargeLevel; }
}
public class Lab4Demo {

    static void displayVehicle(Vehicle vehicle) {
        vehicle.display();
    }

    public static void main(String[] args) {

        // 1. Object Creation
        System.out.println("=== Object Creation ===");
        Vehicle vehicle = new Vehicle("Generic Vehicle", 2020);
        Car car = new Car("Toyota Camry", 2022, 4, 75.0);
        Motorcycle motorcycle = new Motorcycle("Yamaha YZF", 2021, true);
        ElectricCar electricCar = new ElectricCar("Tesla Model 3", 2023, 4, 75.0, 85.0);

        // 2. Direct Method Calls
        System.out.println("\n=== Direct Method Calls ===");
        vehicle.display();
        car.display();
        motorcycle.display();
        electricCar.display();

        // 3. Polymorphism with Superclass References
        System.out.println("\n=== Polymorphic Dispatch ===");
        Vehicle[] vehicles = {vehicle, car, motorcycle, electricCar};
        for (int i = 0; i < vehicles.length; i++) {
            System.out.println("vehicles[" + i + "]:");
            displayVehicle(vehicles[i]);
        }

        // 4. Inherited Methods
        System.out.println("\n=== Inherited Methods ===");
        car.accelerate(50);
        System.out.println("After car.accelerate(50):");
        car.display();
        motorcycle.accelerate(80);
        System.out.println("After motorcycle.accelerate(80):");
        motorcycle.display();
        electricCar.accelerate(40);
        System.out.println("After electricCar.accelerate(40):");
        electricCar.display();

        // 5. Subclass-Specific Methods
        System.out.println("\n=== Subclass-Specific Methods ===");
        car.refuel(20.0);
        System.out.println("After car.refuel(20.0):");
        car.display();
        motorcycle.wheelie();
        electricCar.charge(15.0);
        System.out.println("After electricCar.charge(15.0):");
        electricCar.display();

        // 6. instanceof and Safe Casting
        System.out.println("\n=== instanceof and Safe Casting ===");
        Vehicle v = new Car("Honda Civic", 2021, 2, 60.0);
        System.out.println("v instanceof Vehicle    : " + (v instanceof Vehicle));
        System.out.println("v instanceof Car        : " + (v instanceof Car));
        System.out.println("v instanceof Motorcycle : " + (v instanceof Motorcycle));
        if (v instanceof Car) {
            Car castedCar = (Car) v;
            System.out.println("Number of doors after casting: " + castedCar.getNumDoors());
        }

        // 7. Method Overriding Verification
        System.out.println("\n=== Method Overriding Verification ===");
        Vehicle ref1 = new Car("Ford Mustang", 2020, 2, 50.0);
        Vehicle ref2 = new Motorcycle("Harley-Davidson", 2022, false);
        Vehicle ref3 = new ElectricCar("Nissan Leaf", 2023, 4, 62.0, 70.0);
        ref1.display();
        ref2.display();
        ref3.display();

        // 8. Inherited brake() Method
        System.out.println("\n=== Inherited brake() Method ===");
        Car testCar = new Car("Subaru Outback", 2019, 5, 45.0);
        testCar.accelerate(100);
        System.out.println("After testCar.accelerate(100):");
        testCar.display();
        testCar.brake(30);
        System.out.println("After testCar.brake(30):");
        testCar.display();
        testCar.brake(200);
        System.out.println("After testCar.brake(200):");
        testCar.display();
    }
}

Copy or download each file, save it under the same name, then compile and run: javac *.java → java Lab4Demo

Implementation

Expected output

Shortened: repeated display blocks are summarised. The full output is in the lab manual, section 4.5.2.

Interactive 1 of 2

Reference type vs object type

Interactive 2 of 2

When is a cast safe?

In-lab task 1 of 3

Task 1: Add a Bus class

A new Vehicle subclass that sells tickets.
Requirements
Show a hint
Cap revenue at the number of seats: Math.min(passengersBoarded, numSeats) * ticketPrice.

In-lab task 2 of 3

Task 2: Create a HybridCar class

A Car subclass that can run on fuel or battery.
Requirements
Show a hint
Calling v.switchMode("Battery") on a Vehicle reference will not compile — that is the point of the cast.

In-lab task 3 of 3

Task 3: Test instanceof and casting

Process a mixed array safely.
Requirements
Show a hint
Test the most specific type first: an ElectricCar is also a Car.

In-lab tasks

Show your work during the lab

For each task, show the instructor:

  • the source code
  • a successful compilation
  • the running output
  • a short explanation of the logic you used

Lab report · exercise 1 of 2

Employee hierarchy and payroll

Employee with Manager, Developer and Intern subclasses.
Requirements

Lab report · exercise 2 of 2

Zoo animal classification

Animal → Mammal (Lion, Elephant) and Bird (Eagle).
Requirements

Lab report

Writing the lab report

  • Problem statement and your design (classes, fields, methods)
  • Complete source code, formatted and commented
  • Sample output or screenshots for normal and edge cases
  • A short discussion: what each OOP concept did in your program

Academic integrity

Copying from the internet, classmates, seniors or any other unauthorized source is strictly prohibited. Full marks may be deducted if plagiarism, copied work or academic dishonesty is detected. Complete the tasks, implementation, output analysis and report yourself.

Discussion and analysis

Viva questions (1 of 2)

Why does Car’s constructor start with super(make, year)?
Show the answer
The Vehicle part of the object (make, year, speed) must be initialised first, and only Vehicle’s constructor knows how.
Why are Vehicle’s fields protected instead of private?
Show the answer
So subclasses such as Car can use make, year and speed directly in their display() methods.

Discussion and analysis

Viva questions (2 of 2)

Vehicle v = new Motorcycle(…); v.display(); — which display runs?
Show the answer
Motorcycle’s. The object type decides at run time (dynamic dispatch).
Why can’t v.wheelie() compile?
Show the answer
The compiler only knows v is a Vehicle, and Vehicle has no wheelie(). Check instanceof and cast to Motorcycle first.

Check yourself

Quiz

Summary

Summary

  • extends creates an is-a relationship and reuses superclass code.
  • super(…) initialises the inherited part first.
  • Overriding replaces an inherited method; @Override lets the compiler check it.
  • Superclass references + overriding = polymorphism (dynamic dispatch).
  • Use instanceof before downcasting.