Java Inheritance - Complete Tutorial
Master Java Inheritance: Learn single, multilevel, hierarchical inheritance, super keyword, method overriding, constructors in inheritance, access modifiers, and real-world examples.
Code Reuse
Inherit properties & methods
extends Keyword
Create inheritance relationship
super Keyword
Access parent class members
Method Overriding
Provide specific implementation
1. Introduction to Inheritance
Inheritance is a fundamental concept in Object-Oriented Programming (OOP) that allows a class to inherit properties and methods from another class. The class that inherits is called the subclass (child class), and the class being inherited from is called the superclass (parent class).
Key Terminology
- Superclass/Parent Class: Class being inherited from
- Subclass/Child Class: Class that inherits
- extends Keyword: Creates inheritance relationship
- IS-A Relationship: Subclass IS-A type of superclass
- Code Reusability: Reuse existing code
- Method Overriding: Subclass provides specific implementation
Real-World Analogy
- Vehicle → Car: Car inherits properties from Vehicle
- Animal → Dog: Dog inherits characteristics from Animal
- Employee → Manager: Manager inherits from Employee
- Shape → Circle: Circle is a specific type of Shape
- Device → Smartphone: Smartphone inherits from Device
- Person → Student: Student is a type of Person
The Power of Inheritance
Inheritance promotes code reusability and establishes a natural hierarchy between classes. When you create a subclass, it automatically gets all the non-private fields and methods of the superclass. You can then add new features or modify existing ones in the subclass.
// Superclass (Parent Class)
class Vehicle {
// Instance variables
protected String brand;
protected String model;
protected int year;
protected double price;
// Constructor
public Vehicle(String brand, String model, int year, double price) {
this.brand = brand;
this.model = model;
this.year = year;
this.price = price;
System.out.println("Vehicle constructor called");
}
// Methods
public void start() {
System.out.println(brand + " " + model + " is starting...");
}
public void stop() {
System.out.println(brand + " " + model + " is stopping...");
}
public void displayInfo() {
System.out.println("Brand: " + brand);
System.out.println("Model: " + model);
System.out.println("Year: " + year);
System.out.println("Price: $" + price);
}
// Getters
public String getBrand() { return brand; }
public String getModel() { return model; }
public int getYear() { return year; }
public double getPrice() { return price; }
}
// Subclass (Child Class) - Car inherits from Vehicle
class Car extends Vehicle {
// Additional instance variables specific to Car
private int numberOfDoors;
private String fuelType;
private boolean hasSunroof;
// Constructor
public Car(String brand, String model, int year, double price,
int numberOfDoors, String fuelType, boolean hasSunroof) {
// Call superclass constructor using super()
super(brand, model, year, price);
// Initialize Car-specific properties
this.numberOfDoors = numberOfDoors;
this.fuelType = fuelType;
this.hasSunroof = hasSunroof;
System.out.println("Car constructor called");
}
// Additional methods specific to Car
public void honk() {
System.out.println(brand + " " + model + " is honking: Beep Beep!");
}
public void openSunroof() {
if (hasSunroof) {
System.out.println(brand + " " + model + " sunroof is opening...");
} else {
System.out.println(brand + " " + model + " doesn't have a sunroof");
}
}
// Override displayInfo() method from Vehicle
@Override
public void displayInfo() {
// Call parent class method using super
super.displayInfo();
// Add Car-specific information
System.out.println("Number of Doors: " + numberOfDoors);
System.out.println("Fuel Type: " + fuelType);
System.out.println("Has Sunroof: " + (hasSunroof ? "Yes" : "No"));
}
// Getters for Car-specific properties
public int getNumberOfDoors() { return numberOfDoors; }
public String getFuelType() { return fuelType; }
public boolean hasSunroof() { return hasSunroof; }
}
// Another Subclass - Motorcycle inherits from Vehicle
class Motorcycle extends Vehicle {
// Additional instance variables
private boolean hasSidecar;
private String motorcycleType;
// Constructor
public Motorcycle(String brand, String model, int year, double price,
boolean hasSidecar, String motorcycleType) {
super(brand, model, year, price);
this.hasSidecar = hasSidecar;
this.motorcycleType = motorcycleType;
System.out.println("Motorcycle constructor called");
}
// Additional methods
public void wheelie() {
System.out.println(brand + " " + model + " is doing a wheelie!");
}
// Override start() method
@Override
public void start() {
System.out.println(brand + " " + model + " motorcycle engine is roaring to life!");
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.println("Has Sidecar: " + (hasSidecar ? "Yes" : "No"));
System.out.println("Type: " + motorcycleType);
}
}
public class InheritanceDemo {
public static void main(String[] args) {
System.out.println("=== Inheritance Demonstration ===\n");
// Create a Car object
System.out.println("=== Creating Car Object ===");
Car myCar = new Car("Toyota", "Camry", 2023, 25000.0, 4, "Gasoline", true);
System.out.println("\n=== Car Methods ===");
myCar.start(); // Inherited from Vehicle
myCar.honk(); // Car-specific method
myCar.openSunroof(); // Car-specific method
myCar.stop(); // Inherited from Vehicle
System.out.println("\n=== Car Information ===");
myCar.displayInfo(); // Overridden method
System.out.println("\n=== Creating Motorcycle Object ===");
Motorcycle myBike = new Motorcycle("Harley-Davidson", "Sportster", 2022, 15000.0, false, "Cruiser");
System.out.println("\n=== Motorcycle Methods ===");
myBike.start(); // Overridden method
myBike.wheelie(); // Motorcycle-specific method
myBike.stop(); // Inherited from Vehicle
System.out.println("\n=== Motorcycle Information ===");
myBike.displayInfo(); // Overridden method
System.out.println("\n=== Benefits of Inheritance ===");
System.out.println("1. Code Reusability: Common code in Vehicle class");
System.out.println("2. Extensibility: Easy to add new vehicle types");
System.out.println("3. Maintainability: Changes in Vehicle affect all subclasses");
System.out.println("4. Polymorphism: Vehicles can be treated uniformly");
System.out.println("\n=== IS-A Relationship ===");
System.out.println("Car IS-A Vehicle: " + (myCar instanceof Vehicle));
System.out.println("Motorcycle IS-A Vehicle: " + (myBike instanceof Vehicle));
System.out.println("Car IS-A Car: " + (myCar instanceof Car));
System.out.println("Vehicle IS-A Car: " + (new Vehicle("Generic", "Model", 2020, 10000) instanceof Car));
}
}
Inheritance Hierarchy Visualization
Vehicle is the superclass. Car, Motorcycle, Truck are subclasses. ElectricCar is a subclass of Car (multilevel inheritance).
2. Types of Inheritance in Java
Java supports several types of inheritance through class extension. Understanding these types helps you design better class hierarchies.
Single Inheritance
- One subclass extends one superclass
- Most common type in Java
- Simple and straightforward
- Example: Class B extends Class A
- Prevents diamond problem
- Direct parent-child relationship
Multilevel Inheritance
- Chain of inheritance
- Class C extends Class B, which extends Class A
- Grandparent → Parent → Child relationship
- Example: Animal → Mammal → Dog
- Supports code reuse across generations
- Can access grandparent members via parent
Hierarchical Inheritance
- One superclass, multiple subclasses
- Multiple classes extend the same parent
- Example: Shape → Circle, Square, Triangle
- Promotes code sharing among siblings
- Common interface for related classes
- Enables polymorphism
Multiple Inheritance (NOT Supported)
- One class extends multiple classes
- NOT supported in Java
- Causes diamond problem ambiguity
- Alternative: Use interfaces
- Java 8+: Default methods in interfaces
- Example: Interface A, Interface B
Types of Inheritance Comparison
| Type | Description | Diagram | Supported in Java? | Example |
|---|---|---|---|---|
| Single | One subclass extends one superclass | A → B | Yes | Car extends Vehicle |
| Multilevel | Chain of inheritance (A → B → C) | A → B → C | Yes | Animal → Mammal → Dog |
| Hierarchical | Multiple subclasses extend one superclass | A → B, A → C, A → D | Yes | Shape → Circle, Square, Triangle |
| Multiple | One class extends multiple classes | A → C ← B | No | Not supported (use interfaces) |
| Hybrid | Combination of multiple types | Complex | Partial | Through interfaces only |
public class InheritanceTypes {
// === SINGLE INHERITANCE ===
static class Employee {
private String name;
private int id;
private double salary;
public Employee(String name, int id, double salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
public void work() {
System.out.println(name + " is working");
}
public double calculateBonus() {
return salary * 0.10;
}
}
// Single Inheritance: Manager extends Employee
static class Manager extends Employee {
private int teamSize;
public Manager(String name, int id, double salary, int teamSize) {
super(name, id, salary);
this.teamSize = teamSize;
}
public void conductMeeting() {
System.out.println(getName() + " is conducting a meeting with " + teamSize + " team members");
}
@Override
public double calculateBonus() {
return super.calculateBonus() + (teamSize * 500);
}
public String getName() {
// Accessing parent's private field through getter (not shown)
return "Manager: Access needed";
}
}
// === MULTILEVEL INHERITANCE ===
static class Animal {
protected String species;
public Animal(String species) {
this.species = species;
}
public void eat() {
System.out.println(species + " is eating");
}
public void sleep() {
System.out.println(species + " is sleeping");
}
}
static class Mammal extends Animal {
protected boolean hasFur;
public Mammal(String species, boolean hasFur) {
super(species);
this.hasFur = hasFur;
}
public void giveBirth() {
System.out.println(species + " is giving birth to live young");
}
}
// Multilevel Inheritance: Dog extends Mammal which extends Animal
static class Dog extends Mammal {
private String breed;
public Dog(String breed, boolean hasFur) {
super("Dog", hasFur);
this.breed = breed;
}
public void bark() {
System.out.println(breed + " dog is barking: Woof! Woof!");
}
@Override
public void eat() {
System.out.println(breed + " dog is eating dog food");
}
// Can access grandparent methods
public void animalBehavior() {
super.eat(); // Calls Mammal's eat() which might call Animal's eat()
sleep(); // From Animal class
}
}
// === HIERARCHICAL INHERITANCE ===
static class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
public double calculateArea() {
return 0.0;
}
public double calculatePerimeter() {
return 0.0;
}
public void display() {
System.out.println("Shape color: " + color);
}
}
// Multiple classes extending the same parent (Hierarchical)
static class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
@Override
public double calculatePerimeter() {
return 2 * Math.PI * radius;
}
@Override
public void display() {
super.display();
System.out.println("Shape: Circle, Radius: " + radius);
}
}
static class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(String color, double width, double height) {
super(color);
this.width = width;
this.height = height;
}
@Override
public double calculateArea() {
return width * height;
}
@Override
public double calculatePerimeter() {
return 2 * (width + height);
}
@Override
public void display() {
super.display();
System.out.println("Shape: Rectangle, Width: " + width + ", Height: " + height);
}
}
static class Triangle extends Shape {
private double base;
private double height;
public Triangle(String color, double base, double height) {
super(color);
this.base = base;
this.height = height;
}
@Override
public double calculateArea() {
return 0.5 * base * height;
}
@Override
public double calculatePerimeter() {
// For simplicity, assuming right triangle
double hypotenuse = Math.sqrt(base * base + height * height);
return base + height + hypotenuse;
}
@Override
public void display() {
super.display();
System.out.println("Shape: Triangle, Base: " + base + ", Height: " + height);
}
}
// === WHY NO MULTIPLE INHERITANCE? ===
/*
// This would cause the Diamond Problem
class A {
void display() { System.out.println("A"); }
}
class B extends A {
@Override
void display() { System.out.println("B"); }
}
class C extends A {
@Override
void display() { System.out.println("C"); }
}
// Which display() should D inherit? B's or C's?
// class D extends B, C { } // NOT ALLOWED in Java
// Solution: Use interfaces
interface BInterface {
default void display() { System.out.println("B Interface"); }
}
interface CInterface {
default void display() { System.out.println("C Interface"); }
}
class D implements BInterface, CInterface {
// Must override to resolve conflict
@Override
public void display() {
BInterface.super.display(); // Choose which to call
}
}
*/
public static void main(String[] args) {
System.out.println("=== Types of Inheritance Demonstration ===\n");
System.out.println("=== SINGLE INHERITANCE ===");
Manager mgr = new Manager("Alice", 101, 80000, 5);
mgr.work(); // Inherited from Employee
mgr.conductMeeting(); // Manager-specific
System.out.println("Bonus: $" + mgr.calculateBonus()); // Overridden method
System.out.println("\n=== MULTILEVEL INHERITANCE ===");
Dog myDog = new Dog("Golden Retriever", true);
myDog.eat(); // Overridden in Dog
myDog.sleep(); // From Animal (grandparent)
myDog.giveBirth(); // From Mammal (parent)
myDog.bark(); // Dog-specific
myDog.animalBehavior(); // Demonstrates multi-level access
System.out.println("\n=== HIERARCHICAL INHERITANCE ===");
Shape[] shapes = {
new Circle("Red", 5.0),
new Rectangle("Blue", 4.0, 6.0),
new Triangle("Green", 3.0, 4.0)
};
for (Shape shape : shapes) {
shape.display();
System.out.println("Area: " + shape.calculateArea());
System.out.println("Perimeter: " + shape.calculatePerimeter());
System.out.println();
}
System.out.println("=== Inheritance Chain Analysis ===");
System.out.println("Dog instance of Dog: " + (myDog instanceof Dog));
System.out.println("Dog instance of Mammal: " + (myDog instanceof Mammal));
System.out.println("Dog instance of Animal: " + (myDog instanceof Animal));
System.out.println("Dog instance of Object: " + (myDog instanceof Object));
System.out.println("\n=== Java's Inheritance Limitations ===");
System.out.println("1. Single Class Inheritance: A class can extend only ONE class");
System.out.println("2. Multiple Interface Inheritance: A class can implement MULTIPLE interfaces");
System.out.println("3. All classes implicitly extend Object class");
System.out.println("4. Final classes cannot be extended");
System.out.println("\n=== Best Practices ===");
System.out.println("1. Use inheritance for IS-A relationships");
System.out.println("2. Keep inheritance hierarchies shallow (2-3 levels)");
System.out.println("3. Favor composition over inheritance when appropriate");
System.out.println("4. Use abstract classes for partial implementations");
System.out.println("5. Use interfaces for defining contracts");
}
}
3. super Keyword - Complete Guide
The super keyword in Java is used to refer to the immediate parent class. It has three main uses: calling parent class constructor, accessing parent class methods, and accessing parent class fields.
super() - Constructor Call
- Must be first statement in constructor
- Calls parent class constructor
- If not specified, super() is implicitly called
- Can pass parameters to parent constructor
- Only one super() call per constructor
- Chains constructors up the hierarchy
super.method() - Method Access
- Calls overridden parent method
- Extends rather than replaces parent behavior
- Useful in method overriding
- Can call any accessible parent method
- Maintains parent functionality
- Avoids code duplication
public class SuperKeywordDemo {
// === PARENT CLASS ===
static class Person {
protected String name;
protected int age;
protected String gender;
protected String nationality;
// Constructor 1: Default
public Person() {
this("Unknown", 0, "Unknown", "Unknown");
System.out.println("Person() constructor called");
}
// Constructor 2: With parameters
public Person(String name, int age, String gender, String nationality) {
this.name = name;
this.age = age;
this.gender = gender;
this.nationality = nationality;
System.out.println("Person(String, int, String, String) constructor called");
}
// Constructor 3: Copy constructor
public Person(Person other) {
this(other.name, other.age, other.gender, other.nationality);
System.out.println("Person(Person) copy constructor called");
}
// Methods
public void introduce() {
System.out.println("Hello, I'm " + name + ", " + age + " years old from " + nationality);
}
public void work() {
System.out.println(name + " is working as a person");
}
public void celebrateBirthday() {
age++;
System.out.println(name + " is now " + age + " years old!");
}
// Getters
public String getName() { return name; }
public int getAge() { return age; }
public String getGender() { return gender; }
public String getNationality() { return nationality; }
}
// === CHILD CLASS 1: Student ===
static class Student extends Person {
private String studentId;
private String major;
private double gpa;
// Constructor 1: Using super() to call specific parent constructor
public Student(String name, int age, String gender, String nationality,
String studentId, String major, double gpa) {
// Call parent constructor with super()
super(name, age, gender, nationality);
this.studentId = studentId;
this.major = major;
this.gpa = gpa;
System.out.println("Student constructor called");
}
// Constructor 2: Using default parent constructor
public Student(String studentId, String major) {
// super() is called implicitly (calls Person() default constructor)
this.studentId = studentId;
this.major = major;
this.gpa = 0.0;
System.out.println("Student(String, String) constructor called");
}
// Constructor 3: Copy another student
public Student(Student other) {
// Call parent copy constructor
super(other);
this.studentId = other.studentId;
this.major = other.major;
this.gpa = other.gpa;
System.out.println("Student(Student) copy constructor called");
}
// Override introduce() method
@Override
public void introduce() {
// Call parent's introduce() using super
super.introduce();
// Add Student-specific information
System.out.println("I'm a student studying " + major + " with GPA: " + gpa);
}
// Override work() method
@Override
public void work() {
// Extend parent behavior instead of replacing
super.work(); // Person's work() method
System.out.println(name + " is also studying for exams");
}
// Student-specific method
public void takeExam(String course) {
System.out.println(name + " (ID: " + studentId + ") is taking " + course + " exam");
}
// Method that uses super to access parent's private field through getter
public void displayFullInfo() {
System.out.println("=== Student Information ===");
System.out.println("Name: " + super.getName()); // Using parent's getter
System.out.println("Age: " + super.getAge());
System.out.println("Student ID: " + studentId);
System.out.println("Major: " + major);
System.out.println("GPA: " + gpa);
}
// Getters
public String getStudentId() { return studentId; }
public String getMajor() { return major; }
public double getGpa() { return gpa; }
}
// === CHILD CLASS 2: Employee (demonstrating different super usage) ===
static class Employee extends Person {
private String employeeId;
private String department;
private double salary;
private String position;
// Constructor with super() call
public Employee(String name, int age, String gender, String nationality,
String employeeId, String department, double salary, String position) {
super(name, age, gender, nationality); // Must be first statement
this.employeeId = employeeId;
this.department = department;
this.salary = salary;
this.position = position;
System.out.println("Employee constructor called");
}
// Constructor chaining with this() and super()
public Employee(String name, String employeeId, String department) {
this(name, 25, "Unknown", "Unknown", employeeId, department, 50000.0, "Junior");
System.out.println("Employee(String, String, String) constructor called");
}
// Override introduce() without calling super (completely new behavior)
@Override
public void introduce() {
System.out.println("Professional introduction:");
System.out.println("Hello, I'm " + name + ", " + position + " in " + department);
System.out.println("Employee ID: " + employeeId);
}
// Override work() calling super and adding new behavior
@Override
public void work() {
System.out.println(name + " is working as a " + position);
super.work(); // Still calls Person's work() method
System.out.println("Department: " + department + ", Salary: $" + salary);
}
// Employee-specific method that uses super for parent method
public void requestVacation(int days) {
System.out.println(name + " is requesting " + days + " days vacation");
super.celebrateBirthday(); // Using parent's method
}
// Method showing when super is NOT needed
public void displayInfo() {
// Can directly access protected fields from parent
System.out.println("Employee: " + name);
System.out.println("Age: " + age); // age is protected in Person
// Using super is only needed when there's name conflict
// System.out.println("Name from parent: " + super.name); // Same as name
}
}
// === GRANDCHILD CLASS: Demonstrating multilevel super ===
static class WorkingStudent extends Student {
private String jobTitle;
private double hourlyRate;
public WorkingStudent(String name, int age, String gender, String nationality,
String studentId, String major, double gpa,
String jobTitle, double hourlyRate) {
// Call Student constructor
super(name, age, gender, nationality, studentId, major, gpa);
this.jobTitle = jobTitle;
this.hourlyRate = hourlyRate;
System.out.println("WorkingStudent constructor called");
}
@Override
public void introduce() {
// Call Student's introduce() which calls Person's introduce()
super.introduce();
// Add WorkingStudent-specific info
System.out.println("I also work as a " + jobTitle + " ($" + hourlyRate + "/hr)");
}
@Override
public void work() {
// Can only call immediate parent's method with super
super.work(); // Calls Student's work()
// To call Person's work() directly, need to go through Student
System.out.println("And working part-time as " + jobTitle);
}
public void calculateWeeklyPay(int hours) {
double pay = hours * hourlyRate;
System.out.println(name + " will earn $" + pay + " this week");
}
}
public static void main(String[] args) {
System.out.println("=== super Keyword Demonstration ===\n");
System.out.println("=== Creating Student Object ===");
Student student = new Student("John Doe", 20, "Male", "USA",
"S12345", "Computer Science", 3.8);
System.out.println("\n=== Student Methods ===");
student.introduce(); // Calls overridden method (which calls super.introduce())
student.work(); // Calls overridden method (which calls super.work())
student.takeExam("Java Programming");
student.displayFullInfo();
student.celebrateBirthday(); // Inherited from Person
System.out.println("\n=== Creating Employee Object ===");
Employee employee = new Employee("Alice Smith", 30, "Female", "Canada",
"E67890", "Engineering", 85000.0, "Senior Developer");
System.out.println("\n=== Employee Methods ===");
employee.introduce(); // Completely overridden (no super call)
employee.work(); // Extends parent behavior
employee.requestVacation(10);
System.out.println("\n=== Creating WorkingStudent (Multilevel) ===");
WorkingStudent workingStudent = new WorkingStudent("Bob Johnson", 22, "Male", "UK",
"S54321", "Business", 3.5,
"Teaching Assistant", 25.0);
System.out.println("\n=== WorkingStudent Methods ===");
workingStudent.introduce(); // Calls Student.introduce() → Person.introduce()
workingStudent.work(); // Calls Student.work() → Person.work()
workingStudent.calculateWeeklyPay(20);
System.out.println("\n=== Important Rules for super ===");
System.out.println("1. super() must be the first statement in constructor");
System.out.println("2. super can be used to call parent constructors");
System.out.println("3. super can be used to call parent methods");
System.out.println("4. super cannot be used in static methods");
System.out.println("5. Each constructor can have only one super() call");
System.out.println("6. If no super() is written, compiler adds super()");
System.out.println("\n=== When to Use super ===");
System.out.println("1. When overriding methods but want parent behavior too");
System.out.println("2. When parent has multiple constructors");
System.out.println("3. When child has field with same name as parent");
System.out.println("4. When implementing constructor chaining");
System.out.println("5. When extending abstract class implementations");
System.out.println("\n=== Common Mistakes ===");
System.out.println("1. Forgetting super() call when parent has no default constructor");
System.out.println("2. Calling super() after other statements (compile error)");
System.out.println("3. Using super in static context (not allowed)");
System.out.println("4. Overusing super when not needed");
}
}
4. Constructors in Inheritance
Constructors play a crucial role in inheritance. When an object of a subclass is created, constructors are called in a specific order from top to bottom of the inheritance hierarchy.
Constructor Chaining Order
- Top-down execution: Parent → Child → Grandchild
- Parent constructor executes first
- Implicit super() call if not specified
- Default constructor added by compiler if needed
- Each class responsible for its own initialization
- Final step: Object class constructor
Important Rules
- super() must be first statement in constructor
- If no constructor, compiler adds default
- If parent has no default constructor, must call super() explicitly
- Cannot use both this() and super() in same constructor
- Private constructors prevent inheritance
- Final classes cannot have subclasses
public class ConstructorInheritance {
// === BASE CLASS WITH DIFFERENT CONSTRUCTORS ===
static class A {
private String name;
private int value;
// Constructor 1: Default
public A() {
this("Default", 0); // Call another constructor in same class
System.out.println("A() default constructor");
}
// Constructor 2: With parameters
public A(String name, int value) {
// Implicit call to Object() constructor happens here
this.name = name;
this.value = value;
System.out.println("A(String, int) constructor: " + name + ", " + value);
}
// Constructor 3: Copy constructor
public A(A other) {
this(other.name, other.value);
System.out.println("A(A) copy constructor");
}
// No default constructor if we comment out A() - try it!
// Only parameterized constructor exists
}
// === SUBCLASS B ===
static class B extends A {
private double data;
// Constructor 1: Default - will cause error if A has no default constructor
public B() {
// super() is implicitly called here
// If A has no default constructor, this will cause compile error
this.data = 0.0;
System.out.println("B() default constructor");
}
// Constructor 2: With parameters - explicitly calls parent constructor
public B(String name, int value, double data) {
// Must call parent constructor explicitly
super(name, value); // Calls A(String, int)
this.data = data;
System.out.println("B(String, int, double) constructor: data=" + data);
}
// Constructor 3: Using this() for constructor chaining
public B(double data) {
this("B-default", 100, data); // Calls B(String, int, double)
System.out.println("B(double) constructor");
}
}
// === SUBCLASS C (extends B) ===
static class C extends B {
private boolean flag;
// Constructor 1
public C(String name, int value, double data, boolean flag) {
// Must call B constructor (which calls A constructor)
super(name, value, data);
this.flag = flag;
System.out.println("C(...) constructor: flag=" + flag);
}
// Constructor 2: Demonstrating full constructor chain
public C() {
// Implicit super() calls B() which calls A()
// This only works if all parents have default constructors
this.flag = false;
System.out.println("C() default constructor");
}
}
// === CLASS WITH NO DEFAULT CONSTRUCTOR ===
static class ParentNoDefault {
private int x;
// Only parameterized constructor - NO default constructor
public ParentNoDefault(int x) {
this.x = x;
System.out.println("ParentNoDefault(int) constructor: x=" + x);
}
// If we don't provide default constructor, compiler won't add one
// because we have a parameterized constructor
}
static class ChildMustCallSuper extends ParentNoDefault {
private int y;
public ChildMustCallSuper(int x, int y) {
// MUST call super() explicitly because parent has no default constructor
super(x); // This is required!
this.y = y;
System.out.println("ChildMustCallSuper(int, int) constructor: y=" + y);
}
/*
// This would cause COMPILE ERROR because no super() call
public ChildMustCallSuper(int y) {
this.y = y; // ERROR: constructor ParentNoDefault() is undefined
}
*/
}
// === FINAL CLASS EXAMPLE ===
static final class FinalClass {
// Final class cannot be extended
public FinalClass() {
System.out.println("FinalClass constructor");
}
}
/*
// This would cause COMPILE ERROR
static class TryToExtendFinal extends FinalClass {
// Cannot extend final class
}
*/
// === CLASS WITH PRIVATE CONSTRUCTOR ===
static class PrivateConstructor {
private static int instanceCount = 0;
// Private constructor - cannot be instantiated or extended
private PrivateConstructor() {
instanceCount++;
System.out.println("PrivateConstructor created, total: " + instanceCount);
}
// Factory method pattern
public static PrivateConstructor createInstance() {
return new PrivateConstructor();
}
}
/*
// This would cause COMPILE ERROR
static class TryToExtendPrivate extends PrivateConstructor {
// Cannot extend class with private constructor
}
*/
// === ABSTRACT CLASS CONSTRUCTORS ===
abstract class AbstractBase {
protected String type;
// Abstract classes CAN have constructors
public AbstractBase(String type) {
this.type = type;
System.out.println("AbstractBase constructor: " + type);
}
// Abstract method
public abstract void performAction();
// Concrete method
public void displayType() {
System.out.println("Type: " + type);
}
}
static class ConcreteChild extends AbstractBase {
private int value;
public ConcreteChild(String type, int value) {
// Must call parent constructor
super(type);
this.value = value;
System.out.println("ConcreteChild constructor: value=" + value);
}
@Override
public void performAction() {
System.out.println("ConcreteChild performing action with value: " + value);
}
}
public static void main(String[] args) {
System.out.println("=== Constructor Inheritance Demonstration ===\n");
System.out.println("=== Simple Inheritance Chain ===");
System.out.println("\nCreating C object with parameters:");
C c1 = new C("Test", 42, 3.14, true);
System.out.println("\nCreating C object with default constructor:");
C c2 = new C();
System.out.println("\n=== Constructor with No Default Parent ===");
ChildMustCallSuper child = new ChildMustCallSuper(10, 20);
System.out.println("\n=== Abstract Class Constructor ===");
ConcreteChild concrete = new ConcreteChild("CustomType", 100);
concrete.performAction();
concrete.displayType();
System.out.println("\n=== Factory Pattern (Private Constructor) ===");
PrivateConstructor p1 = PrivateConstructor.createInstance();
PrivateConstructor p2 = PrivateConstructor.createInstance();
System.out.println("\n=== Constructor Execution Order ===");
System.out.println("When creating new C(\"Test\", 42, 3.14, true):");
System.out.println("1. Object() constructor (implicit)");
System.out.println("2. A(String, int) constructor");
System.out.println("3. B(String, int, double) constructor");
System.out.println("4. C(...) constructor");
System.out.println("\n=== Key Rules Demonstrated ===");
System.out.println("1. Constructors execute from top to bottom (parent to child)");
System.out.println("2. super() must be first statement in constructor");
System.out.println("3. If no constructor is defined, compiler adds default");
System.out.println("4. If parent has no default constructor, child MUST call super()");
System.out.println("5. Cannot use both this() and super() in same constructor");
System.out.println("6. Abstract classes can have constructors");
System.out.println("7. Final classes cannot be extended");
System.out.println("8. Private constructors prevent inheritance");
System.out.println("\n=== Common Issues and Solutions ===");
System.out.println("Problem: 'constructor X in class Y cannot be applied to given types'");
System.out.println("Solution: Call correct parent constructor with super(...)");
System.out.println("\nProblem: 'constructor call must be first statement'");
System.out.println("Solution: Move super() or this() to beginning of constructor");
System.out.println("\nProblem: 'class Z has private constructor'");
System.out.println("Solution: Use factory method or change constructor visibility");
System.out.println("\n=== Best Practices ===");
System.out.println("1. Provide default constructors in base classes");
System.out.println("2. Use super() explicitly for clarity");
System.out.println("3. Document constructor requirements");
System.out.println("4. Consider making classes final if not designed for extension");
System.out.println("5. Use factory methods for complex object creation");
}
}
5. Method Overriding in Inheritance
Method overriding allows a subclass to provide a specific implementation of a method that is already defined in its superclass. This is a key feature that enables polymorphism in Java.
import java.util.ArrayList;
import java.util.List;
public class MethodOverridingInheritance {
// === BASE BANK ACCOUNT CLASS ===
static class BankAccount {
protected String accountNumber;
protected String accountHolder;
protected double balance;
public BankAccount(String accountNumber, String accountHolder, double initialBalance) {
this.accountNumber = accountNumber;
this.accountHolder = accountHolder;
this.balance = initialBalance;
}
// Methods to be potentially overridden
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println(accountHolder + ": Deposited $" + amount);
} else {
System.out.println("Invalid deposit amount");
}
}
public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println(accountHolder + ": Withdrew $" + amount);
return true;
} else {
System.out.println(accountHolder + ": Withdrawal failed - insufficient funds");
return false;
}
}
public double calculateInterest() {
// Base class provides default implementation
return 0.0; // No interest by default
}
public void displayAccountInfo() {
System.out.println("=== Account Information ===");
System.out.println("Account Holder: " + accountHolder);
System.out.println("Account Number: " + accountNumber);
System.out.println("Balance: $" + balance);
System.out.println("Interest Earned: $" + calculateInterest());
}
// Final method - cannot be overridden
public final String getAccountNumber() {
return accountNumber;
}
// Getters
public String getAccountHolder() { return accountHolder; }
public double getBalance() { return balance; }
}
// === SAVINGS ACCOUNT (Overrides calculateInterest) ===
static class SavingsAccount extends BankAccount {
private double interestRate;
private double minimumBalance;
public SavingsAccount(String accountNumber, String accountHolder,
double initialBalance, double interestRate, double minimumBalance) {
super(accountNumber, accountHolder, initialBalance);
this.interestRate = interestRate;
this.minimumBalance = minimumBalance;
}
// Override withdraw() to enforce minimum balance
@Override
public boolean withdraw(double amount) {
if (balance - amount >= minimumBalance) {
return super.withdraw(amount); // Call parent's withdraw
} else {
System.out.println(accountHolder + ": Withdrawal denied - below minimum balance");
return false;
}
}
// Override calculateInterest() with savings-specific calculation
@Override
public double calculateInterest() {
return balance * (interestRate / 100);
}
// Override displayAccountInfo() to show savings-specific info
@Override
public void displayAccountInfo() {
super.displayAccountInfo(); // Call parent's display
System.out.println("Account Type: Savings");
System.out.println("Interest Rate: " + interestRate + "%");
System.out.println("Minimum Balance: $" + minimumBalance);
}
// Cannot override final method getAccountNumber()
// @Override public String getAccountNumber() { return "SAV-" + accountNumber; } // ERROR
}
// === CHECKING ACCOUNT (Overrides differently) ===
static class CheckingAccount extends BankAccount {
private int freeTransactions;
private int transactionCount;
private double transactionFee;
public CheckingAccount(String accountNumber, String accountHolder,
double initialBalance, int freeTransactions, double transactionFee) {
super(accountNumber, accountHolder, initialBalance);
this.freeTransactions = freeTransactions;
this.transactionFee = transactionFee;
this.transactionCount = 0;
}
// Override deposit() to count transactions
@Override
public void deposit(double amount) {
super.deposit(amount);
transactionCount++;
applyTransactionFee();
}
// Override withdraw() to count transactions
@Override
public boolean withdraw(double amount) {
boolean success = super.withdraw(amount);
if (success) {
transactionCount++;
applyTransactionFee();
}
return success;
}
private void applyTransactionFee() {
if (transactionCount > freeTransactions) {
balance -= transactionFee;
System.out.println(accountHolder + ": Transaction fee charged: $" + transactionFee);
}
}
// Override displayAccountInfo()
@Override
public void displayAccountInfo() {
super.displayAccountInfo();
System.out.println("Account Type: Checking");
System.out.println("Free Transactions: " + freeTransactions);
System.out.println("Transactions Used: " + transactionCount);
System.out.println("Transaction Fee: $" + transactionFee);
}
// New method specific to CheckingAccount
public void resetTransactionCount() {
transactionCount = 0;
System.out.println(accountHolder + ": Transaction count reset");
}
}
// === FIXED DEPOSIT ACCOUNT ===
static class FixedDepositAccount extends BankAccount {
private int termMonths;
private double fixedInterestRate;
private boolean prematureWithdrawalAllowed;
public FixedDepositAccount(String accountNumber, String accountHolder,
double initialBalance, int termMonths,
double fixedInterestRate, boolean prematureWithdrawalAllowed) {
super(accountNumber, accountHolder, initialBalance);
this.termMonths = termMonths;
this.fixedInterestRate = fixedInterestRate;
this.prematureWithdrawalAllowed = prematureWithdrawalAllowed;
}
// Override withdraw() with term restrictions
@Override
public boolean withdraw(double amount) {
if (!prematureWithdrawalAllowed) {
System.out.println(accountHolder + ": Premature withdrawal not allowed");
return false;
}
// Apply penalty for premature withdrawal
double penalty = amount * 0.05; // 5% penalty
if (super.withdraw(amount + penalty)) {
System.out.println(accountHolder + ": Premature withdrawal penalty: $" + penalty);
return true;
}
return false;
}
// Override calculateInterest() with fixed deposit calculation
@Override
public double calculateInterest() {
return balance * (fixedInterestRate / 100) * (termMonths / 12.0);
}
// Cannot override - method is private in parent (not inherited)
// @Override private void somePrivateMethod() { } // Not possible
// Static method - not overridden, but hidden
public static String getBankName() {
return "Fixed Deposit Bank";
}
}
// === CREDIT ACCOUNT (Demonstrating covariant return types) ===
static class CreditAccount extends BankAccount {
private double creditLimit;
private double creditUsed;
public CreditAccount(String accountNumber, String accountHolder,
double creditLimit) {
super(accountNumber, accountHolder, 0); // Balance starts at 0
this.creditLimit = creditLimit;
this.creditUsed = 0;
}
// Override getBalance() with covariant return type (same return type)
@Override
public double getBalance() {
// For credit account, balance is negative (amount owed)
return creditUsed;
}
// Get available credit
public double getAvailableCredit() {
return creditLimit - creditUsed;
}
// Override withdraw() for credit account (increases debt)
@Override
public boolean withdraw(double amount) {
if (creditUsed + amount <= creditLimit) {
creditUsed += amount;
System.out.println(accountHolder + ": Credit used: $" + amount);
System.out.println("Available credit: $" + getAvailableCredit());
return true;
} else {
System.out.println(accountHolder + ": Credit limit exceeded");
return false;
}
}
// Override deposit() to reduce debt
@Override
public void deposit(double amount) {
if (amount > 0) {
creditUsed = Math.max(0, creditUsed - amount);
System.out.println(accountHolder + ": Credit paid: $" + amount);
System.out.println("Remaining debt: $" + creditUsed);
}
}
@Override
public void displayAccountInfo() {
System.out.println("=== Credit Account ===");
System.out.println("Account Holder: " + accountHolder);
System.out.println("Account Number: " + accountNumber);
System.out.println("Credit Limit: $" + creditLimit);
System.out.println("Credit Used: $" + creditUsed);
System.out.println("Available Credit: $" + getAvailableCredit());
}
}
// === BANK SYSTEM DEMONSTRATING POLYMORPHISM ===
static class Bank {
private List accounts;
public Bank() {
accounts = new ArrayList<>();
}
public void addAccount(BankAccount account) {
accounts.add(account);
}
public void processMonthlyInterest() {
System.out.println("\n=== Processing Monthly Interest ===");
for (BankAccount account : accounts) {
double interest = account.calculateInterest();
if (interest > 0) {
account.deposit(interest);
System.out.println(account.getAccountHolder() +
": Interest added: $" + interest);
}
}
}
public void displayAllAccounts() {
System.out.println("\n=== All Bank Accounts ===");
for (BankAccount account : accounts) {
account.displayAccountInfo();
System.out.println();
}
}
public double getTotalBankAssets() {
double total = 0;
for (BankAccount account : accounts) {
total += account.getBalance();
}
return total;
}
}
public static void main(String[] args) {
System.out.println("=== Method Overriding in Inheritance ===\n");
// Create bank
Bank bank = new Bank();
// Create different types of accounts
SavingsAccount savings = new SavingsAccount("SAV001", "Alice Johnson",
5000.0, 3.5, 1000.0);
CheckingAccount checking = new CheckingAccount("CHK001", "Bob Smith",
2500.0, 5, 2.0);
FixedDepositAccount fixedDeposit = new FixedDepositAccount("FD001", "Charlie Brown",
10000.0, 12, 5.0, false);
CreditAccount credit = new CreditAccount("CRD001", "Diana Miller", 5000.0);
// Add accounts to bank
bank.addAccount(savings);
bank.addAccount(checking);
bank.addAccount(fixedDeposit);
bank.addAccount(credit);
// Demonstrate polymorphic behavior
System.out.println("=== Transaction Demonstrations ===");
System.out.println("\n--- Savings Account Transactions ---");
savings.deposit(1000.0);
savings.withdraw(4500.0); // Should succeed
savings.withdraw(1000.0); // Should fail (below minimum)
System.out.println("\n--- Checking Account Transactions ---");
checking.deposit(500.0);
checking.withdraw(200.0);
checking.withdraw(100.0); // 3rd transaction (still free)
checking.withdraw(50.0); // 4th transaction (still free)
checking.withdraw(50.0); // 5th transaction (free limit reached)
checking.withdraw(50.0); // 6th transaction (should charge fee)
checking.resetTransactionCount();
System.out.println("\n--- Fixed Deposit Account ---");
fixedDeposit.withdraw(1000.0); // Should fail (not allowed)
System.out.println("\n--- Credit Account ---");
credit.withdraw(2000.0); // Use credit
credit.withdraw(3500.0); // Use more credit
credit.withdraw(1000.0); // Should fail (exceeds limit)
credit.deposit(1000.0); // Pay down credit
// Process interest for all accounts (polymorphic call)
bank.processMonthlyInterest();
// Display all accounts (polymorphic call)
bank.displayAllAccounts();
System.out.println("=== Total Bank Assets: $" + bank.getTotalBankAssets() + " ===");
System.out.println("\n=== Method Overriding Rules Demonstrated ===");
System.out.println("1. Same method signature (name + parameters)");
System.out.println("2. Return type should be same or covariant (subtype)");
System.out.println("3. Access modifier cannot be more restrictive");
System.out.println("4. Cannot override final, static, or private methods");
System.out.println("5. Can throw same, subclass, or no exception (not broader)");
System.out.println("\n=== @Override Annotation Benefits ===");
System.out.println("1. Compile-time checking of override correctness");
System.out.println("2. Clearly indicates intent to override");
System.out.println("3. Prevents accidental method hiding");
System.out.println("4. Improves code readability");
System.out.println("\n=== When to Override Methods ===");
System.out.println("1. When subclass needs different behavior");
System.out.println("2. When extending framework classes");
System.out.println("3. When implementing template method pattern");
System.out.println("4. When providing specific implementations in inheritance hierarchy");
System.out.println("\n=== When NOT to Override ===");
System.out.println("1. When method is marked final");
System.out.println("2. When method is static (it's hidden, not overridden)");
System.out.println("3. When you want to completely replace without calling super");
System.out.println("4. When parent implementation is sufficient");
}
}
6. Real-World Example: E-commerce System
Complete E-commerce Inheritance Hierarchy
// Complete e-commerce system demonstrating inheritance
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class ECommerceInheritance {
// === BASE CLASS: Product ===
static abstract class Product {
protected String id;
protected String name;
protected String description;
protected double price;
protected int stockQuantity;
protected String category;
protected double weight;
public Product(String id, String name, String description,
double price, int stockQuantity, String category, double weight) {
this.id = id;
this.name = name;
this.description = description;
this.price = price;
this.stockQuantity = stockQuantity;
this.category = category;
this.weight = weight;
}
// Abstract methods - must be implemented by subclasses
public abstract double calculateShippingCost();
public abstract int getWarrantyMonths();
public abstract String getProductType();
// Concrete methods - common to all products
public boolean isAvailable() {
return stockQuantity > 0;
}
public boolean purchase(int quantity) {
if (quantity <= stockQuantity) {
stockQuantity -= quantity;
return true;
}
return false;
}
public void restock(int quantity) {
stockQuantity += quantity;
}
public double calculateTotalCost(int quantity) {
return (price * quantity) + calculateShippingCost();
}
public void displayProductInfo() {
System.out.println("=== Product Information ===");
System.out.println("ID: " + id);
System.out.println("Name: " + name);
System.out.println("Type: " + getProductType());
System.out.println("Category: " + category);
System.out.println("Price: $" + price);
System.out.println("Stock: " + stockQuantity);
System.out.println("Available: " + (isAvailable() ? "Yes" : "No"));
System.out.println("Warranty: " + getWarrantyMonths() + " months");
System.out.println("Shipping Cost: $" + calculateShippingCost());
System.out.println("Weight: " + weight + " kg");
}
// Getters
public String getId() { return id; }
public String getName() { return name; }
public double getPrice() { return price; }
public int getStockQuantity() { return stockQuantity; }
public String getCategory() { return category; }
}
// === ELECTRONICS PRODUCTS ===
static abstract class ElectronicsProduct extends Product {
protected String brand;
protected String model;
protected int powerConsumption; // in watts
public ElectronicsProduct(String id, String name, String description,
double price, int stockQuantity, String category, double weight,
String brand, String model, int powerConsumption) {
super(id, name, description, price, stockQuantity, category, weight);
this.brand = brand;
this.model = model;
this.powerConsumption = powerConsumption;
}
@Override
public double calculateShippingCost() {
// Electronics have higher shipping cost due to fragility
return weight * 3.0 + 10.0; // $10 handling fee
}
@Override
public void displayProductInfo() {
super.displayProductInfo();
System.out.println("Brand: " + brand);
System.out.println("Model: " + model);
System.out.println("Power Consumption: " + powerConsumption + "W");
}
}
// Smartphone - extends ElectronicsProduct
static class Smartphone extends ElectronicsProduct {
private String os;
private int storageGB;
private int ramGB;
private double screenSize;
public Smartphone(String id, String name, String description,
double price, int stockQuantity, double weight,
String brand, String model, int powerConsumption,
String os, int storageGB, int ramGB, double screenSize) {
super(id, name, description, price, stockQuantity, "Smartphone", weight,
brand, model, powerConsumption);
this.os = os;
this.storageGB = storageGB;
this.ramGB = ramGB;
this.screenSize = screenSize;
}
@Override
public int getWarrantyMonths() {
return 24; // 2-year warranty for smartphones
}
@Override
public String getProductType() {
return "Smartphone";
}
@Override
public void displayProductInfo() {
super.displayProductInfo();
System.out.println("Operating System: " + os);
System.out.println("Storage: " + storageGB + "GB");
System.out.println("RAM: " + ramGB + "GB");
System.out.println("Screen Size: " + screenSize + " inches");
}
// Smartphone-specific method
public void makeCall(String number) {
System.out.println(name + " calling " + number + "...");
}
}
// Laptop - extends ElectronicsProduct
static class Laptop extends ElectronicsProduct {
private String processor;
private int storageGB;
private int ramGB;
private boolean hasDedicatedGPU;
public Laptop(String id, String name, String description,
double price, int stockQuantity, double weight,
String brand, String model, int powerConsumption,
String processor, int storageGB, int ramGB, boolean hasDedicatedGPU) {
super(id, name, description, price, stockQuantity, "Laptop", weight,
brand, model, powerConsumption);
this.processor = processor;
this.storageGB = storageGB;
this.ramGB = ramGB;
this.hasDedicatedGPU = hasDedicatedGPU;
}
@Override
public int getWarrantyMonths() {
return 36; // 3-year warranty for laptops
}
@Override
public String getProductType() {
return "Laptop";
}
@Override
public double calculateShippingCost() {
// Laptops require special packaging
return super.calculateShippingCost() + 15.0;
}
@Override
public void displayProductInfo() {
super.displayProductInfo();
System.out.println("Processor: " + processor);
System.out.println("Storage: " + storageGB + "GB");
System.out.println("RAM: " + ramGB + "GB");
System.out.println("Dedicated GPU: " + (hasDedicatedGPU ? "Yes" : "No"));
}
// Laptop-specific method
public void runBenchmark() {
System.out.println(name + " running performance benchmark...");
}
}
// === CLOTHING PRODUCTS ===
static abstract class ClothingProduct extends Product {
protected String size;
protected String color;
protected String material;
public ClothingProduct(String id, String name, String description,
double price, int stockQuantity, String category, double weight,
String size, String color, String material) {
super(id, name, description, price, stockQuantity, category, weight);
this.size = size;
this.color = color;
this.material = material;
}
@Override
public double calculateShippingCost() {
// Clothing has lower shipping cost
return weight * 1.5;
}
@Override
public void displayProductInfo() {
super.displayProductInfo();
System.out.println("Size: " + size);
System.out.println("Color: " + color);
System.out.println("Material: " + material);
}
}
// TShirt - extends ClothingProduct
static class TShirt extends ClothingProduct {
private String sleeveType;
private String neckType;
public TShirt(String id, String name, String description,
double price, int stockQuantity, double weight,
String size, String color, String material,
String sleeveType, String neckType) {
super(id, name, description, price, stockQuantity, "T-Shirt", weight,
size, color, material);
this.sleeveType = sleeveType;
this.neckType = neckType;
}
@Override
public int getWarrantyMonths() {
return 0; // No warranty for clothing
}
@Override
public String getProductType() {
return "T-Shirt";
}
@Override
public void displayProductInfo() {
super.displayProductInfo();
System.out.println("Sleeve Type: " + sleeveType);
System.out.println("Neck Type: " + neckType);
}
// TShirt-specific method
public void washInstructions() {
System.out.println("Wash in cold water, tumble dry low");
}
}
// Jeans - extends ClothingProduct
static class Jeans extends ClothingProduct {
private String fitType;
private String washType;
public Jeans(String id, String name, String description,
double price, int stockQuantity, double weight,
String size, String color, String material,
String fitType, String washType) {
super(id, name, description, price, stockQuantity, "Jeans", weight,
size, color, material);
this.fitType = fitType;
this.washType = washType;
}
@Override
public int getWarrantyMonths() {
return 0; // No warranty for clothing
}
@Override
public String getProductType() {
return "Jeans";
}
@Override
public double calculateShippingCost() {
// Jeans are heavier than other clothing
return weight * 2.0;
}
@Override
public void displayProductInfo() {
super.displayProductInfo();
System.out.println("Fit Type: " + fitType);
System.out.println("Wash Type: " + washType);
}
}
// === BOOK PRODUCTS ===
static class Book extends Product {
private String author;
private String isbn;
private int numberOfPages;
private String publisher;
public Book(String id, String name, String description,
double price, int stockQuantity, double weight,
String author, String isbn, int numberOfPages, String publisher) {
super(id, name, description, price, stockQuantity, "Book", weight);
this.author = author;
this.isbn = isbn;
this.numberOfPages = numberOfPages;
this.publisher = publisher;
}
@Override
public double calculateShippingCost() {
// Books have very low shipping cost
return weight * 1.0;
}
@Override
public int getWarrantyMonths() {
return 0; // No warranty for books
}
@Override
public String getProductType() {
return "Book";
}
@Override
public void displayProductInfo() {
super.displayProductInfo();
System.out.println("Author: " + author);
System.out.println("ISBN: " + isbn);
System.out.println("Pages: " + numberOfPages);
System.out.println("Publisher: " + publisher);
}
// Book-specific method
public void readSample() {
System.out.println("Reading sample from '" + name + "' by " + author);
}
}
// === E-COMMERCE STORE ===
static class ECommerceStore {
private List products;
private String storeName;
public ECommerceStore(String storeName) {
this.storeName = storeName;
products = new ArrayList<>();
}
public void addProduct(Product product) {
products.add(product);
}
public Product findProductById(String id) {
for (Product product : products) {
if (product.getId().equals(id)) {
return product;
}
}
return null;
}
public void displayAllProducts() {
System.out.println("\n=== " + storeName + " - All Products ===");
for (Product product : products) {
product.displayProductInfo();
System.out.println();
}
}
public void displayProductsByCategory(String category) {
System.out.println("\n=== Products in Category: " + category + " ===");
for (Product product : products) {
if (product.getCategory().equals(category)) {
product.displayProductInfo();
System.out.println();
}
}
}
public double calculateOrderTotal(List productIds, List quantities) {
double total = 0;
for (int i = 0; i < productIds.size(); i++) {
Product product = findProductById(productIds.get(i));
if (product != null && product.isAvailable()) {
total += product.calculateTotalCost(quantities.get(i));
}
}
return total;
}
public boolean processOrder(List productIds, List quantities) {
System.out.println("\n=== Processing Order ===");
for (int i = 0; i < productIds.size(); i++) {
Product product = findProductById(productIds.get(i));
if (product == null || !product.purchase(quantities.get(i))) {
System.out.println("Failed to purchase: " + productIds.get(i));
return false;
}
System.out.println("Purchased " + quantities.get(i) + " of " + product.getName());
}
return true;
}
public double getTotalInventoryValue() {
double total = 0;
for (Product product : products) {
total += product.getPrice() * product.getStockQuantity();
}
return total;
}
}
public static void main(String[] args) {
System.out.println("=== E-Commerce System with Inheritance ===\n");
// Create store
ECommerceStore store = new ECommerceStore("TechFashion Books");
// Create products using inheritance hierarchy
Smartphone phone = new Smartphone("PHN001", "Galaxy S24", "Flagship smartphone",
999.99, 50, 0.2, "Samsung", "S24", 15,
"Android", 256, 12, 6.7);
Laptop laptop = new Laptop("LPT001", "MacBook Pro", "Professional laptop",
2499.99, 25, 2.1, "Apple", "M3 Pro", 65,
"M3 Pro", 1000, 36, true);
TShirt tshirt = new TShirt("TSH001", "Cotton T-Shirt", "100% cotton t-shirt",
29.99, 100, 0.3, "L", "Black", "Cotton",
"Short Sleeve", "Crew Neck");
Jeans jeans = new Jeans("JNS001", "Slim Fit Jeans", "Denim jeans",
89.99, 75, 0.8, "32x32", "Blue", "Denim",
"Slim Fit", "Stone Wash");
Book book = new Book("BOK001", "Clean Code", "Software development book",
39.99, 200, 0.5, "Robert C. Martin", "978-0132350884",
464, "Prentice Hall");
// Add products to store
store.addProduct(phone);
store.addProduct(laptop);
store.addProduct(tshirt);
store.addProduct(jeans);
store.addProduct(book);
// Display all products
store.displayAllProducts();
// Display by category
store.displayProductsByCategory("Smartphone");
store.displayProductsByCategory("T-Shirt");
// Demonstrate polymorphic behavior
System.out.println("=== Product-Specific Behaviors ===");
phone.makeCall("123-456-7890");
laptop.runBenchmark();
tshirt.washInstructions();
book.readSample();
// Process an order
List orderProductIds = List.of("PHN001", "TSH001", "BOK001");
List orderQuantities = List.of(1, 2, 1);
double orderTotal = store.calculateOrderTotal(orderProductIds, orderQuantities);
System.out.println("\nOrder Total (including shipping): $" + orderTotal);
boolean orderSuccess = store.processOrder(orderProductIds, orderQuantities);
System.out.println("Order Status: " + (orderSuccess ? "Success" : "Failed"));
// Display updated inventory
System.out.println("\n=== Updated Inventory ===");
store.displayProductsByCategory("Smartphone");
store.displayProductsByCategory("T-Shirt");
System.out.println("=== Total Inventory Value: $" + store.getTotalInventoryValue() + " ===");
System.out.println("\n=== Inheritance Benefits Demonstrated ===");
System.out.println("1. Code Reuse: Common product functionality in base class");
System.out.println("2. Extensibility: Easy to add new product types");
System.out.println("3. Polymorphism: Store works with Product interface");
System.out.println("4. Maintainability: Changes in base class affect all products");
System.out.println("5. Type Safety: Compile-time checking of product types");
System.out.println("\n=== Inheritance Hierarchy Summary ===");
System.out.println("Product (abstract)");
System.out.println("├── ElectronicsProduct (abstract)");
System.out.println("│ ├── Smartphone");
System.out.println("│ └── Laptop");
System.out.println("├── ClothingProduct (abstract)");
System.out.println("│ ├── TShirt");
System.out.println("│ └── Jeans");
System.out.println("└── Book");
}
}
7. Best Practices & Common Pitfalls
- Violating Liskov Substitution Principle: Subclass changes parent behavior unexpectedly
- Deep Inheritance Hierarchies: Too many levels make code hard to understand
- Inheritance for Code Reuse Only: Using inheritance when composition is better
- Ignoring super() Calls: Forgetting to call parent constructor
- Overusing Protected: Exposing too much implementation detail
- Circular Dependencies: Classes depending on each other in hierarchy
Inheritance Best Practices
- Use inheritance only for true IS-A relationships
- Keep inheritance hierarchies shallow (2-3 levels)
- Favor composition over inheritance when possible
- Make classes final unless designed for extension
- Use abstract classes for partial implementations
- Document intended extension points
- Test subclasses independently
Design Principles
- Liskov Substitution: Subclasses should be substitutable
- Open/Closed: Open for extension, closed for modification
- Interface Segregation: Small, focused interfaces
- Dependency Inversion: Depend on abstractions
- Single Responsibility: One reason to change
- Composition over Inheritance: More flexible design
Inheritance vs Composition
Use Inheritance when: True IS-A relationship exists, you need polymorphism, framework requires extension
Use Composition when: You want to reuse code, relationship is HAS-A, need runtime flexibility
Remember: "Favor composition over inheritance" - Design Patterns, Gang of Four
public class InheritanceChecklist {
public static void inheritanceChecklist() {
System.out.println("=== Inheritance Implementation Checklist ===\n");
System.out.println("✅ 1. IS-A Relationship Check");
System.out.println(" - Does Subclass IS-A Superclass?");
System.out.println(" - Example: Dog IS-A Animal (YES)");
System.out.println(" - Example: Engine IS-A Car (NO - use composition)");
System.out.println("\n✅ 2. Liskov Substitution Principle");
System.out.println(" - Can subclass replace superclass without issues?");
System.out.println(" - No strengthened preconditions");
System.out.println(" - No weakened postconditions");
System.out.println(" - Preserves invariants");
System.out.println("\n✅ 3. Constructor Design");
System.out.println(" - Does parent have appropriate constructors?");
System.out.println(" - Are super() calls correct?");
System.out.println(" - Consider making classes final if not for extension");
System.out.println("\n✅ 4. Access Modifier Review");
System.out.println(" - Use private for implementation details");
System.out.println(" - Use protected only for intended extension points");
System.out.println(" - Public methods define the API");
System.out.println("\n✅ 5. Method Overriding");
System.out.println(" - Use @Override annotation");
System.out.println(" - Maintain method contracts");
System.out.println(" - Call super.method() when extending behavior");
System.out.println("\n✅ 6. Testing Considerations");
System.out.println(" - Test subclasses independently");
System.out.println(" - Test polymorphic behavior");
System.out.println(" - Test edge cases in inheritance chain");
System.out.println("\n✅ 7. Documentation");
System.out.println(" - Document extension points");
System.out.println(" - Note any restrictions or requirements");
System.out.println(" - Provide examples of proper usage");
}
// Example: Proper inheritance design
abstract class Notification {
protected String recipient;
protected String message;
public Notification(String recipient, String message) {
this.recipient = recipient;
this.message = message;
}
// Template method pattern
public final void send() {
validate();
prepare();
deliver();
log();
}
protected void validate() {
if (recipient == null || recipient.isEmpty()) {
throw new IllegalArgumentException("Recipient is required");
}
if (message == null || message.isEmpty()) {
throw new IllegalArgumentException("Message is required");
}
}
protected void prepare() {
System.out.println("Preparing notification for: " + recipient);
}
// Abstract method - subclasses must implement
protected abstract void deliver();
protected void log() {
System.out.println("Notification logged: " + getClass().getSimpleName());
}
// Factory method
public static Notification createEmail(String recipient, String message) {
return new EmailNotification(recipient, message);
}
}
static class EmailNotification extends Notification {
public EmailNotification(String recipient, String message) {
super(recipient, message);
}
@Override
protected void deliver() {
System.out.println("Sending email to: " + recipient);
System.out.println("Message: " + message);
}
@Override
protected void prepare() {
super.prepare();
System.out.println("Setting up SMTP connection");
}
}
static class SMSNotification extends Notification {
public SMSNotification(String recipient, String message) {
super(recipient, message);
}
@Override
protected void deliver() {
System.out.println("Sending SMS to: " + recipient);
System.out.println("Message: " + (message.length() > 160 ? message.substring(0, 160) : message));
}
}
static class PushNotification extends Notification {
private String deviceId;
public PushNotification(String recipient, String message, String deviceId) {
super(recipient, message);
this.deviceId = deviceId;
}
@Override
protected void validate() {
super.validate();
if (deviceId == null || deviceId.isEmpty()) {
throw new IllegalArgumentException("Device ID is required for push notifications");
}
}
@Override
protected void deliver() {
System.out.println("Sending push to device: " + deviceId);
System.out.println("Title: " + recipient);
System.out.println("Body: " + message);
}
}
public static void main(String[] args) {
inheritanceChecklist();
System.out.println("\n=== Example: Well-Designed Inheritance ===");
Notification[] notifications = {
Notification.createEmail("user@example.com", "Welcome to our service!"),
new SMSNotification("+1234567890", "Your verification code is 123456"),
new PushNotification("App Update", "New version available", "device-abc123")
};
System.out.println("\n=== Sending All Notifications ===");
for (Notification notification : notifications) {
try {
notification.send();
System.out.println();
} catch (Exception e) {
System.out.println("Failed to send: " + e.getMessage());
}
}
System.out.println("=== Key Design Principles Demonstrated ===");
System.out.println("1. Template Method: send() defines algorithm skeleton");
System.out.println("2. Liskov Substitution: All notifications are substitutable");
System.out.println("3. Open/Closed: Easy to add new notification types");
System.out.println("4. Single Responsibility: Each class has one purpose");
System.out.println("5. Factory Method: createEmail() for object creation");
System.out.println("\n=== When to Use Inheritance ===");
System.out.println("✅ Modeling IS-A relationships");
System.out.println("✅ Framework extension points");
System.out.println("✅ Template method pattern");
System.out.println("✅ When polymorphism is needed");
System.out.println("\n=== When to Avoid Inheritance ===");
System.out.println("❌ Just for code reuse (use composition)");
System.out.println("❌ When relationship is HAS-A, not IS-A");
System.out.println("❌ When you need multiple inheritance (use interfaces)");
System.out.println("❌ When classes might change independently");
System.out.println("\n=== Final Recommendations ===");
System.out.println("1. Start with interfaces for maximum flexibility");
System.out.println("2. Use abstract classes for shared implementation");
System.out.println("3. Keep inheritance hierarchies shallow");
System.out.println("4. Document for extension, design for restriction");
System.out.println("5. Test inheritance relationships thoroughly");
}
}