Chapter 10: Inheritance

Introduction to Inheritance

Inheritance is used to borrow properties & methods from an existing class.

It allows us to create new classes based on existing classes, promoting code reusability and establishing relationships between classes.

Basic Concept

📱 Phone

Super Class

🤖 Smart Phone

Sub Class

Subclass extends Superclass

Vehicle Hierarchy

Vehicle
Car
Truck

Animal Hierarchy

Animal
Dog
Cat

Important: Java doesn't support multiple inheritance (i.e., two classes cannot be super classes for a subclass).

Declaring Inheritance in Java

Inheritance in Java is declared using the extends keyword.

InheritanceExample.java
// Base class (Superclass)
class Animal {
    String name;
    int age;
    
    void eat() {
        System.out.println(name + " is eating");
    }
    
    void sleep() {
        System.out.println(name + " is sleeping");
    }
}

// Derived class (Subclass)
class Dog extends Animal {
    String breed;
    
    void bark() {
        System.out.println(name + " is barking");
    }
    
    void wagTail() {
        System.out.println(name + " is wagging tail");
    }
}

public class InheritanceExample {
    public static void main(String[] args) {
        Dog myDog = new Dog();
        myDog.name = "Buddy";
        myDog.age = 3;
        myDog.breed = "Golden Retriever";
        
        // Using inherited methods
        myDog.eat();    // From Animal class
        myDog.sleep();  // From Animal class
        
        // Using Dog's own methods
        myDog.bark();
        myDog.wagTail();
    }
}

When a class inherits from a superclass, it inherits:

  • All public and protected fields
  • All public and protected methods
  • Package-private members (if in same package)
  • Constructors in Inheritance

    When a derived class is extended from the base class, the constructor of the base class is executed first followed by the constructor of the derived class.

    1

    Parent Constructor

    Base class constructor executes first

    2

    Child Constructor

    Derived class constructor executes second

    3

    Grand Child Constructor

    Further derived class constructor executes last

    ConstructorInheritance.java
    class Parent {
        Parent() {
            System.out.println("Parent constructor called");
        }
        
        Parent(String name) {
            System.out.println("Parent constructor with name: " + name);
        }
    }
    
    class Child extends Parent {
        Child() {
            System.out.println("Child constructor called");
        }
        
        Child(String name) {
            super(name); // Call parent constructor with parameter
            System.out.println("Child constructor with name: " + name);
        }
    }
    
    public class ConstructorInheritance {
        public static void main(String[] args) {
            System.out.println("Creating Child object:");
            Child child = new Child();
            
            System.out.println("\nCreating Child object with name:");
            Child namedChild = new Child("John");
        }
    }

    Constructor Overloading: When there are multiple constructors in the parent class, the constructor without any parameters is called from the child class by default.

    Super Keyword

    A reference variable used to refer to the immediate parent class object.

    Instance Variables

    Can be used to refer immediate parent class instance variable

    super.variableName

    Methods

    Can be used to invoke parent class methods

    super.methodName()

    Constructors

    Can be used to invoke parent class constructors

    super(parameters)
    SuperKeywordExample.java
    class Vehicle {
        String brand = "Generic";
        int maxSpeed = 100;
        
        void start() {
            System.out.println("Vehicle is starting");
        }
        
        void displayInfo() {
            System.out.println("Brand: " + brand + ", Max Speed: " + maxSpeed);
        }
    }
    
    class Car extends Vehicle {
        String brand = "Toyota"; // Hiding parent variable
        int doors = 4;
        
        void start() {
            super.start(); // Call parent method
            System.out.println("Car engine started");
        }
        
        void displayInfo() {
            System.out.println("Car Brand: " + brand);
            System.out.println("Vehicle Brand: " + super.brand); // Access parent variable
            super.displayInfo(); // Call parent method
            System.out.println("Doors: " + doors);
        }
    }
    
    public class SuperKeywordExample {
        public static void main(String[] args) {
            Car car = new Car();
            car.start();
            System.out.println();
            car.displayInfo();
        }
    }

    Method Overriding

    If the child class implements the same method present in the parent class again, it is known as method overriding.

    This is also called "Redefining method of super class in sub class".

    MethodOverriding.java
    class Animal {
        void makeSound() {
            System.out.println("Animal makes a sound");
        }
        
        void move() {
            System.out.println("Animal moves");
        }
    }
    
    class Dog extends Animal {
        @Override
        void makeSound() {
            System.out.println("Dog barks: Woof! Woof!");
        }
        
        @Override
        void move() {
            System.out.println("Dog runs on four legs");
        }
    }
    
    class Cat extends Animal {
        @Override
        void makeSound() {
            System.out.println("Cat meows: Meow! Meow!");
        }
        
        @Override
        void move() {
            System.out.println("Cat walks silently");
        }
    }
    
    public class MethodOverriding {
        public static void main(String[] args) {
            Animal animal = new Animal();
            Dog dog = new Dog();
            Cat cat = new Cat();
            
            animal.makeSound(); // Animal makes a sound
            dog.makeSound();    // Dog barks: Woof! Woof!
            cat.makeSound();    // Cat meows: Meow! Meow!
            
            animal.move(); // Animal moves
            dog.move();    // Dog runs on four legs
            cat.move();    // Cat walks silently
        }
    }

    Rules for Method Overriding:

  • Method name must be the same
  • Parameter list must be the same
  • Return type must be the same or covariant
  • Access modifier cannot be more restrictive
  • Cannot override static, final, or private methods
  • Dynamic Method Dispatch

    Dynamic method dispatch is used to achieve runtime polymorphism in Java.

    It allows Java to decide which method to call at runtime based on the actual object type.

    Scenario 1: Allowed

    Super obj = new Sub(); obj.method1(); // Sub's method called obj.method3(); // Not Allowed - method doesn't exist in Super

    Result: Method of actual object (Sub) is called

    Scenario 2: Not Allowed

    Sub obj = new Super();

    Result: Compilation error - cannot assign parent to child reference

    DynamicDispatch.java
    class Shape {
        void draw() {
            System.out.println("Drawing a shape");
        }
        
        void area() {
            System.out.println("Calculating area of shape");
        }
    }
    
    class Circle extends Shape {
        @Override
        void draw() {
            System.out.println("Drawing a circle");
        }
        
        @Override
        void area() {
            System.out.println("Area = π × r²");
        }
        
        void circumference() {
            System.out.println("Circumference = 2 × π × r");
        }
    }
    
    class Rectangle extends Shape {
        @Override
        void draw() {
            System.out.println("Drawing a rectangle");
        }
        
        @Override
        void area() {
            System.out.println("Area = length × width");
        }
    }
    
    public class DynamicDispatch {
        public static void main(String[] args) {
            // Dynamic method dispatch
            Shape shape1 = new Circle();    // Allowed
            Shape shape2 = new Rectangle(); // Allowed
            
            shape1.draw(); // Calls Circle's draw method
            shape1.area(); // Calls Circle's area method
            
            shape2.draw(); // Calls Rectangle's draw method
            shape2.area(); // Calls Rectangle's area method
            
            // shape1.circumference(); // Not allowed - method not in Shape class
            
            // To call circumference, we need to cast
            if (shape1 instanceof Circle) {
                ((Circle) shape1).circumference();
            }
        }
    }

    Practice Set

    Chapter 10 - Practice Set

    1. Circle and Cylinder

    Create a class Circle and use inheritance to create another class Cylinder from it.

    2. Rectangle and Cuboid

    Create a class Rectangle and use inheritance to create another class Cuboid. Try to keep it as close to real world scenario as possible.

    3. Area and Volume Methods

    Create methods for area and volume in problem 1.

    4. Getters and Setters

    Create methods for area & volume in problem 2. Also create getters and setters.

    5. Constructor Execution Order

    What is the order of constructor execution for the following inheritance hierarchy?
    Base → Derived1 → Derived2
    Derived2 obj = new Derived2();
    Which constructor(s) will be executed & in what order?

    Solution Example: Circle and Cylinder

    ← Previous: Constructors Next: Abstract & Interfaces →