inheritance and polymorphism
The Four Pillars of OOP
Java's object-oriented model rests on four core principles. We touched on encapsulation in the last topic — now let's cover the rest.
| Pillar | Meaning |
|---|---|
| Encapsulation | Hiding internal data behind private fields and public methods |
| Inheritance | A class acquiring fields and methods from another class |
| Polymorphism | The same method behaving differently depending on the object |
| Abstraction | Hiding complex implementation details behind a simple interface |
Inheritance
Inheritance lets one class (the subclass) reuse fields and methods from another (the superclass), using the extends keyword. This avoids duplicating code between related classes.
// Superclass
public class Animal {
String name;
Animal(String name) {
this.name = name;
}
void eat() {
System.out.println(name + " is eating.");
}
}
// Subclass
public class Dog extends Animal {
Dog(String name) {
super(name); // calls the Animal constructor
}
void bark() {
System.out.println(name + " says Woof!");
}
}Dog myDog = new Dog("Rex");
myDog.eat(); // inherited from Animal: "Rex is eating."
myDog.bark(); // defined in Dog: "Rex says Woof!"
Note:super(name)calls the constructor of the parent class. It must be the first line inside the subclass constructor.
Method Overriding
A subclass can provide its own version of a method that already exists in the superclass, using the @Override annotation.
public class Animal {
void makeSound() {
System.out.println("Some generic animal sound");
}
}
public class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Meow");
}
}Animal genericAnimal = new Animal();
Animal myCat = new Cat();
genericAnimal.makeSound(); // Some generic animal sound
myCat.makeSound(); // MeowPolymorphism in Action
Polymorphism means "many forms" — the same reference type can behave differently depending on the actual object it points to at runtime. This is often shown by storing subclass objects in a superclass-typed array or list.
Animal[] animals = { new Dog("Rex"), new Cat("Whiskers"), new Animal("Generic") };
for (Animal a : animals) {
a.makeSound(); // calls the correct overridden version for each object
}Abstract Classes
An abstract class cannot be instantiated directly — it exists to be extended. It can define abstract methods (no body) that subclasses must implement.
abstract class Shape {
abstract double area(); // no implementation here
void describe() {
System.out.println("This shape has an area of " + area());
}
}
class Circle extends Shape {
double radius;
Circle(double radius) {
this.radius = radius;
}
@Override
double area() {
return Math.PI * radius * radius;
}
}Circle c = new Circle(5);
c.describe(); // This shape has an area of 78.53981633974483Interfaces
An interface defines a contract — a set of method signatures that any implementing class must provide. Unlike abstract classes, a class can implement multiple interfaces.
interface Flyable {
void fly();
}
class Bird implements Flyable {
@Override
public void fly() {
System.out.println("The bird flies through the sky.");
}
}Inheritance vs Interface — Quick Comparison
| Abstract Class | Interface |
|---|---|
| Can have both implemented and abstract methods | Traditionally only method signatures (Java 8+ allows default methods) |
| A class can extend only one abstract class | A class can implement multiple interfaces |
| Use when classes share common code | Use when unrelated classes need to share a capability |
Coming up next: Exception handling — how to gracefully deal with errors when things go wrong at runtime.