Chapter 7: Inheritance and Polymorphism β The True Power of OOP
By Ali Naqi β’ January 15, 2026
𧬠Chapter 7: Inheritance and Polymorphism β The True Power of OOP
1. Inheritance: The "Is-A" Relationship
Inheritance is a mechanism where one class acquires the properties (fields) and behaviors (methods) of another.
Think of it like a family tree. You might inherit your eye color from your parents. In Java, we have:
- The Superclass (Parent): The class whose features are inherited.
- The Subclass (Child): The class that inherits those features.
The extends Keyword
In Java, we use the word extends to establish this relationship.
Java
// The Superclass
class Animal {
String name;
void eat() {
System.out.println(name + " is eating.");
}
}
// The Subclass
class Dog extends Animal {
void bark() {
System.out.println("Woof! Woof!");
}
}
Because Dog extends Animal, a Dog object automatically has a name variable and an eat() method, even though we didn't define them inside the Dog class.
Java
Dog myDog = new Dog(); myDog.name = "Buddy"; myDog.eat(); // Inherited from Animal myDog.bark(); // Defined in Dog
2. The super Keyword: Calling the Parent
What if the parent class has a constructor? As we learned in Chapter 6, constructors aren't inherited like regular methods. However, the child class must call the parent's constructor to properly initialize the inherited fields.
We use the keyword super() to do this.
Java
class Animal {
String name;
Animal(String name) {
this.name = name;
}
}
class Dog extends Animal {
String breed;
Dog(String name, String breed) {
super(name); // Must be the FIRST line in the child constructor!
this.breed = breed;
}
}
3. Method Overriding: Changing the Rules
Sometimes, a child class doesn't want to do things exactly like the parent. For example, all animals makeSound(), but a Cat's sound is different from a Dog's.
Method Overriding allows a subclass to provide a specific implementation of a method that is already provided by its superclass.
Java
class Animal {
void makeSound() {
System.out.println("Some generic animal sound");
}
}
class Cat extends Animal {
@Override // This annotation tells Java we are intentionally replacing the parent's method
void makeSound() {
System.out.println("Meow!");
}
}
Overloading vs. Overriding: Don't confuse the two!
- Overloading (Chapter 4) is same name, different parameters (within the same class).
- Overriding is same name, same parameters, but in a child class.
4. Polymorphism: "Many Forms"
If Inheritance is the "how," Polymorphism is the "wow." The word literally means "many forms." In Java, it means that a single object can be treated as an instance of its own class, its parent class, its grandparent class, and so on.
Dynamic Method Dispatch
This is the heart of polymorphism. You can store a child object in a parent-type variable.
Java
Animal myPet = new Dog("Rex", "German Shepherd");
myPet.makeSound();
Wait, which makeSound() runs? Even though the variable type is Animal, the actual object on the "Heap" (the memory) is a Dog. Java is smart enough to look at the actual object at runtime and say, "Hey, this is a Dog, so I'll use the Dog's bark!"
Why is this useful?
Imagine you have an array of different animals. With polymorphism, you can make them all "perform" without knowing exactly what they are:
Java
Animal[] zoo = { new Dog("Buddy", "Lab"), new Cat("Luna"), new Animal("Generic") };
for (Animal a : zoo) {
a.makeSound(); // Each animal responds in its own way!
}
If you added a Lion class later, you wouldn't have to change your "for loop" at all. This makes your code extensible.
5. Upcasting and Downcasting
- Upcasting: Treating a Child as a Parent (e.g., Animal a = new Dog();). This is always safe and happens automatically.
- Downcasting: Treating a Parent as a Child (e.g., Dog d = (Dog) a;). This is risky. If the animal is actually a Cat and you try to cast it to a Dog, Java will throw a ClassCastException.
To stay safe, always use the instanceof operator:
Java
if (myPet instanceof Dog) {
Dog d = (Dog) myPet;
d.bark();
}
6. The final Keyword: Stopping the Chain
Sometimes, you want to put a stop to inheritance.
- Final Class: If you declare a class as final, no other class can extend it. (Example: The String class in Java is final).
- Final Method: If you declare a method as final, a subclass cannot override it.
Java
public final class SecretBase {
// No one can "extend" the secret base!
}
7. Abstract Classes: The "Incomplete" Blueprint
Sometimes, a parent class is so generic that it shouldn't ever be an object itself. For example, what does a "Shape" look like? You can't draw a "Shape"; you can only draw a Circle or a Square.
We use the abstract keyword to define classes that cannot be instantiated (you can't say new Shape()).
Java
abstract class Shape {
abstract void draw(); // No body! The children MUST provide the implementation.
}
8. Practical Example: A Payment System
Letβs apply these concepts to a real-world scenario: a checkout system that handles different payment methods.
Java
abstract class Payment {
double amount;
Payment(double amount) {
this.amount = amount;
}
// Every payment must implement this
abstract void processPayment();
}
class CreditCard extends Payment {
String cardNumber;
CreditCard(double amount, String cardNumber) {
super(amount);
this.cardNumber = cardNumber;
}
@Override
void processPayment() {
System.out.println("Processing Credit Card payment of $" + amount + " using card " + cardNumber);
}
}
class PayPal extends Payment {
String email;
PayPal(double amount, String email) {
super(amount);
this.email = email;
}
@Override
void processPayment() {
System.out.println("Processing PayPal payment of $" + amount + " for account: " + email);
}
}
public class CheckoutSystem {
public static void main(String[] args) {
// Polymorphism in action!
Payment p1 = new CreditCard(150.0, "1234-5678-9012");
Payment p2 = new PayPal(45.50, "user@example.com");
process(p1);
process(p2);
}
// This method doesn't care if it's CC or PayPal
public static void process(Payment p) {
p.processPayment();
}
}
π Chapter Summary
In this chapter, we unlocked the "Professional" level of Java programming:
- Inheritance (extends) lets us reuse code and build hierarchies.
- Method Overriding allows children to redefine parent behaviors.
- The super keyword gives us access to parent constructors and methods.
- Polymorphism allows us to write flexible code that works with any subclass of a parent.
- Abstract Classes act as strict templates for other classes to follow.
You now understand how large-scale Java applications are organized. By grouping common logic in parent classes and specializing in child classes, you can build systems that are incredibly easy to grow and maintain.
But there is one more piece to the OOP puzzle. Classes can inherit from only one parent. What if we want a class to behave like multiple different things?