Inheritance, Polymorphism, and Interfaces

Class: CSCE-314


Notes:

Up to now, we’ve looked at classes, objects, and encapsulation. Each class has represented a single concept — like a Student, a BankAccount, or a Car. But what happens when we notice patterns across classes? Many share behavior or data, and rewriting the same code violates our DRY (Don’t Repeat Yourself) principle.

That’s where inheritance, polymorphism, and interfaces come in. Together, they let us design families of related classes that share code, yet can specialize and behave differently when needed.

1. Inheritance: Building on Existing Classes

Inheritance lets one class (the subclass) reuse and extend another class (the superclass).

// Base class (superclass)
public class Vehicle {
	protected String brand;
	protected int year;
	
	public Vehicle(String brand, int year) {
		this.brand = brand;
		this.year = year;
	}
	
	public void startEngine() {
		System.out.println("The vehicle's engine starts...");
	}
}

// Derived class (subclass)
public class Car extends Vehicle {
	private int doors;
	
	public Car(String brand, int year, int doors) {
		super(brand, year); // Call superclass constructor
		this.doors = doors;
	}
	
	@Override
	public void startEngine() {
		System.out.println("The car engine roars to life!");
	}
	
	public void honk() {
		System.out.println("Beep! Beep!");
	}
}

Vehicle v1 = new Vehicle("Generic", 2010);
Car c1 = new Car("Toyota", 2021, 4);
v1.startEngine(); // The vehicle's engine starts...
c1.startEngine(); // The car engine roars to life!

2. Polymorphism: Many Forms of Behavior

Polymorphism literally means “many forms.” When we use a superclass reference to refer to a subclass object, we can call the methods defined in the superclass — and if they’re overridden, the subclass’s version runs instead.

Vehicle myVehicle = new Car("Honda", 2022, 4);
myVehicle.startEngine(); // The car engine roars to life!

Vehicle[] garage = {
	new Car("Toyota", 2021, 4),
	new Truck("Ford", 2018, 2),
	new Motorcycle("Yamaha", 2022)
};

for (Vehicle v : garage) {
	v.startEngine(); // Calls each subclass’s version
}

3. Abstract Classes: Defining a Template for Subclasses

public abstract class Animal {
	protected String name;
	
	public Animal(String name) {
		this.name = name;
	}
	
	public abstract void makeSound(); // Must be implemented by subclasses
}

public class Dog extends Animal {
	public Dog(String name) { super(name); }
	
	@Override
	public void makeSound() {
		System.out.println(name + " says: Woof!");
	}
}

4. Interfaces: Defining Behavior without Implementation

Interface: a class that says "I do not care how you implement this set of functions just that you have to have them in order to use my interface"

public interface Drivable {
	void accelerate();
	void brake();
}

public class Bicycle implements Drivable {
	public void accelerate() {
		System.out.println("Pedaling faster!");
	}
	public void brake() {
		System.out.println("Applying the hand brakes!");
	}
}

List<Drivable> thingsToDrive = List.of(new Bicycle(), new Car("Tesla", 2023, 4));

for (Drivable d : thingsToDrive) {
	d.accelerate();
}

5. Design Considerations and Best Practices

  1. Favor composition over inheritance — Sometimes a class should use another rather than extend it.
  2. Use inheritance for “is-a” relationships — A Car is a Vehicle, but a Garage is not a Vehicle.
  3. Mark overridden methods with @Override — This catches mistakes at compile time.
  4. Keep superclass fields protected or private — Maintain encapsulation and provide access through methods.
  5. Interfaces define roles — A class can “play” multiple roles by implementing multiple interfaces.
  6. Abstract classes define common state and partial behavior — Use them when you need both shared code and required methods.

6. Mini Example: Animals and Interfaces

public interface Pet {
	void beFriendly();
}

public abstract class Animal {
	public abstract void makeSound();
}

public class Dog extends Animal implements Pet {
	@Override
	public void makeSound() { System.out.println("Woof!"); }
	public void beFriendly() { System.out.println("Wags tail."); }
}

public class Cat extends Animal implements Pet {
	@Override
	public void makeSound() { System.out.println("Meow!"); }
	public void beFriendly() { System.out.println("Purrs softly."); }
}

List<Pet> pets = List.of(new Dog(), new Cat());
for (Pet p : pets) {
	p.beFriendly();
}

Summary

By the end of this week, you should be able to:

Further Reading in Java Java Java: Object-Oriented Problem Solving (4th Edition)

You can explore these chapters and sections in the Open Textbook Library edition:

https://open.umn.edu/opentextbooks/textbooks/java-java-java-object-oriented-problem-solving