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
- Theory recap
- Walk through the program, section by section, and run it
- In-lab tasks, shown to the instructor
- Report exercises at home
Lab 4 of 10
Contents
Select a part to jump to it.
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.protectedfields (make, year, speed) are visible to subclasses; subclass-only fields stayprivate.
Theory 3 of 5
Overriding vs overloading
| Overloading | Overriding | |
|---|---|---|
| Where | same class | subclass |
| Signature | different parameters | same name, parameters, return type |
| Chosen | at compile time | at run time (object type) |
| Example | display() / display(String) | Car.display() replaces Vehicle.display() |
@Overridemakes 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
instanceofbefore casting to avoid aClassCastException.
Implementation design
Execution flow
- Create a Vehicle, a Car, a Motorcycle and an ElectricCar
- Call display() on each directly
- Store them in a Vehicle[] and display them polymorphically
- Call inherited accelerate() and brake()
- Call subclass-only refuel(), wheelie() and charge()
- Test instanceof and cast a Vehicle reference to Car
- Verify overriding through Vehicle references
- 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;
}protectedfields 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();
@Overridelets 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
=== Object Creation === === Direct Method Calls === ------- Vehicle ------- Make : Generic Vehicle Year : 2020 Speed : 0 km/h ----------------------- ------- Car ------- Make : Toyota Camry Year : 2022 Speed : 0 km/h Doors : 4 Fuel Level: 75.0% ------------------- ------- Motorcycle ------- Make : Yamaha YZF Year : 2021 Speed : 0 km/h Has Storage : Yes -------------------------- ------- Electric Car ------- Make : Tesla Model 3 Year : 2023 Speed : 0 km/h Doors : 4 Battery Capacity : 75.0 kWh Charge Level : 85.0% ---------------------------- === Polymorphic Dispatch === vehicles[0]: ------- Vehicle ------- … (the same four blocks again, one per array element) === Inherited Methods === After car.accelerate(50): Speed : 50 km/h After motorcycle.accelerate(80): Speed : 80 km/h After electricCar.accelerate(40): Speed : 40 km/h === Subclass-Specific Methods === After car.refuel(20.0): Fuel Level: 95.0% Yamaha YZF is performing a wheelie! After electricCar.charge(15.0): Charge Level : 100.0% === instanceof and Safe Casting === v instanceof Vehicle : true v instanceof Car : true v instanceof Motorcycle : false Number of doors after casting: 2 === Method Overriding Verification === ------- Car ------- (Ford Mustang) ------- Motorcycle ------- (Harley-Davidson, Has Storage : No) ------- Electric Car ------- (Nissan Leaf, 62.0 kWh, 70.0%) === Inherited brake() Method === After testCar.accelerate(100): Speed : 100 km/h After testCar.brake(30): Speed : 70 km/h After testCar.brake(200): Speed : 0 km/h
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
Show a hint
Math.min(passengersBoarded, numSeats) * ticketPrice.In-lab task 2 of 3
Task 2: Create a HybridCar class
Show a hint
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
Show a hint
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
Lab report · exercise 2 of 2
Zoo animal classification
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)
super(make, year)?Show the answer
Show the answer
Discussion and analysis
Viva questions (2 of 2)
Vehicle v = new Motorcycle(…); v.display(); — which display runs?Show the answer
v.wheelie() compile?Show the answer
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.