Java Inheritance
Java - Inheritance
Section titled “Java - Inheritance”Inheritance is a fundamental pillar of Object-Oriented Programming (OOP) in Java. It allows a class (the subclass or child class) to inherit fields and methods from another class (the superclass or parent class).
This mechanism promotes code reuse (avoiding duplication) and establishes a hierarchical relationship between classes, often referred to as an “IS-A” relationship.
The “IS-A” Relationship (Class Inheritance)
Section titled “The “IS-A” Relationship (Class Inheritance)”The primary keyword for class inheritance in Java is extends. When a class B extends class A, it means that B IS-A type of A. The subclass B inherits all non-private members (fields and methods) of the superclass A.
Example Hierarchy:
// Superclassclass Vehicle { String brand; int year;
public Vehicle(String brand, int year) { this.brand = brand; this.year = year; }
void displayInfo() { System.out.println("Brand: " + brand + ", Year: " + year); }
void startEngine() { System.out.println("Vehicle engine starting..."); }}
// Subclass inheriting from Vehicleclass Car extends Vehicle { int numberOfDoors;
public Car(String brand, int year, int doors) { // Call the superclass constructor using super() // Must be the first statement in the subclass constructor super(brand, year); this.numberOfDoors = doors; }
// Method specific to Car void honk() { System.out.println("Car horn: Beep beep!"); }
// Overriding a method from the superclass @Override // Good practice annotation void startEngine() { System.out.println("Car engine starting with a key turn..."); }}
// Another subclassclass Bicycle extends Vehicle { // Bicycles can also be considered Vehicles in this model boolean hasBell;
public Bicycle(String brand, int year, boolean bell) { super(brand, year); this.hasBell = bell; }
@Override void startEngine() { System.out.println("Bicycle doesn't have an engine!"); // Specific behavior }
void ringBell() { if(hasBell) { System.out.println("Bicycle bell: Ring ring!"); } else { System.out.println("This bicycle has no bell."); } }}In this example:
Vehicleis the superclass.CarandBicycleare subclasses ofVehicle.CarIS-AVehicle. It inheritsbrand,year,displayInfo(), andstartEngine()fromVehicle.Caradds its own field (numberOfDoors) and method (honk()).Caroverrides thestartEngine()method to provide a car-specific implementation.Bicyclealso IS-AVehicleand overridesstartEngine()differently.- Subclasses use
super(arguments)to call the superclass constructor. - The
@Overrideannotation indicates the intent to override a superclass method and helps the compiler catch errors (e.g., typos in the method signature).
The instanceof Operator
Section titled “The instanceof Operator”The instanceof operator checks if an object is an instance of a particular class or an instance of a subclass of that class.
public class InheritanceCheck { public static void main(String[] args) { Vehicle generalVehicle = new Vehicle("Generic", 2020); Car myCar = new Car("Toyota", 2022, 4); Bicycle myBike = new Bicycle("Giant", 2023, true);
System.out.println("myCar instanceof Car: " + (myCar instanceof Car)); // true System.out.println("myCar instanceof Vehicle: " + (myCar instanceof Vehicle)); // true (IS-A relationship) System.out.println("myBike instanceof Bicycle: " + (myBike instanceof Bicycle)); // true System.out.println("myBike instanceof Vehicle: " + (myBike instanceof Vehicle)); // true (IS-A relationship) System.out.println("generalVehicle instanceof Car: " + (generalVehicle instanceof Car)); // false }}Polymorphism via Inheritance
Section titled “Polymorphism via Inheritance”Inheritance enables polymorphism. You can refer to a subclass object using a superclass reference variable. When you call an overridden method on the superclass reference, Java executes the subclass’s version of the method (dynamic method dispatch).
// Continuing the InheritanceCheck example:
Vehicle vehicleRef1 = myCar; // Car object referenced by Vehicle variableVehicle vehicleRef2 = myBike; // Bicycle object referenced by Vehicle variable
vehicleRef1.startEngine(); // Calls Car's overridden startEngine() -> Car engine starting...vehicleRef2.startEngine(); // Calls Bicycle's overridden startEngine() -> Bicycle doesn't have an engine!
vehicleRef1.displayInfo(); // Calls Vehicle's displayInfo() (not overridden)// vehicleRef1.honk(); // Compile Error! honk() is not defined in the Vehicle class.Inheritance and Interfaces (The implements Keyword)
Section titled “Inheritance and Interfaces (The implements Keyword)”While a class can extend only one superclass (Java supports single class inheritance), it can implement multiple interfaces.
An interface defines a contract - a set of abstract methods (and potentially default/static methods since Java 8) that implementing classes must provide.
Example using Interface:
interface Drivable { void drive(); // Abstract method (no body) int MAX_SPEED = 120; // Constants are implicitly public static final}
interface Chargeable { void charge();}
// Car now extends Vehicle and implements Drivableclass ElectricCar extends Vehicle implements Drivable, Chargeable { int batteryCapacity;
public ElectricCar(String brand, int year, int capacity) { super(brand, year); this.batteryCapacity = capacity; }
@Override public void drive() { // Must implement drive() from Drivable System.out.println("Electric car is driving silently..."); }
@Override public void charge() { // Must implement charge() from Chargeable System.out.println("Plugging in the electric car to charge..."); }
@Override // Override from Vehicle void startEngine() { System.out.println("Electric car motor starting up quietly..."); }}
public class InterfaceCheck { public static void main(String[] args) { ElectricCar tesla = new ElectricCar("Tesla", 2023, 100);
System.out.println("tesla instanceof ElectricCar: " + (tesla instanceof ElectricCar)); // true System.out.println("tesla instanceof Vehicle: " + (tesla instanceof Vehicle)); // true (via extends) System.out.println("tesla instanceof Drivable: " + (tesla instanceof Drivable)); // true (via implements) System.out.println("tesla instanceof Chargeable: " + (tesla instanceof Chargeable)); // true (via implements)
tesla.startEngine(); tesla.drive(); tesla.charge(); }}Implementing interfaces also establishes an IS-A relationship (e.g., ElectricCar IS-A Drivable).
The “HAS-A” Relationship (Composition)
Section titled “The “HAS-A” Relationship (Composition)”While inheritance represents an “IS-A” relationship, sometimes you need to model a “HAS-A” relationship. This is achieved through composition, where a class contains an instance (a field) of another class.
Composition is often preferred over inheritance when the relationship isn’t strictly “IS-A”, as it offers more flexibility and avoids tight coupling.
Example: A Car HAS-A Engine
class Engine { private String type; private int horsepower;
public Engine(String type, int hp) { this.type = type; this.horsepower = hp; }
public void start() { System.out.println("Engine (" + type + ", " + horsepower + "hp) starting..."); }
public void stop() { System.out.println("Engine stopping..."); }}
// Car class using composition (HAS-A Engine)class CarWithEngine { private String make; private String model; private Engine engine; // Car HAS-A Engine
public CarWithEngine(String make, String model, Engine engine) { this.make = make; this.model = model; this.engine = engine; // Assign the Engine object }
public void startCar() { System.out.println("Starting the " + make + " " + model + "."); engine.start(); // Delegate the start action to the Engine object }
public void stopCar() { System.out.println("Stopping the " + make + " " + model + "."); engine.stop(); // Delegate stop action }
// Getter for the engine if needed public Engine getEngine() { return engine; }}
public class CompositionDemo { public static void main(String[] args) { Engine v6Engine = new Engine("V6", 300); CarWithEngine mySedan = new CarWithEngine("Honda", "Accord", v6Engine);
mySedan.startCar(); mySedan.stopCar(); }}Here, CarWithEngine doesn’t inherit from Engine. Instead, it holds a reference to an Engine object and delegates engine-related tasks to it. This promotes code reuse (the Engine class can be used elsewhere) and flexibility.
Key Takeaways
Section titled “Key Takeaways”- Inheritance (
extends) models an “IS-A” relationship, promoting code reuse and polymorphism. - Java supports single class inheritance but multiple interface implementation (
implements). - Use
super()to call superclass constructors andsuper.methodName()to call superclass methods. - Use
@Overridewhen intending to override a superclass method. - Composition (having an instance of another class as a field) models a “HAS-A” relationship and is often favored for flexibility.
instanceofchecks the type relationship between an object and a class/interface.