The inheritance allows defining a class based on another and specializing its behavior.
In programming, we try to avoid unnecessary duplication, although inheritance is not the only way to reuse code.
Imagine you’re programming a video game. You create an Enemy class that has health and position. Then you create a Player that has… health and position. And then an NPC that has… health and position.
Are you going to write int health; int x; int y; three times? And if later you want to change how position works? Are you going to edit three files?
Inheritance allows us to create a base class and have other derived classes receive its accessible members.
The “is-a” Concept
Inheritance establishes a hierarchical relationship.
- A Car is a Vehicle.
- A Cat is an Animal.
- A Manager is an Employee.
If you can’t say the phrase “is a,” you probably shouldn’t use inheritance (maybe you should use composition, “has a,” but that’s an advanced topic).
The extends Keyword
In Java, to inherit we use extends. Let’s see it with the classic vehicle example.
The Superclass (Parent)
We define what is common to all.
// File: Vehiculo.java (Note: Class name kept as example; ideally translated to Vehicle)
public class Vehiculo {
protected String brand; // protected: Children can touch it
protected double speed;
public Vehiculo(String brand) {
this.brand = brand;
this.speed = 0;
}
public void accelerate() {
this.speed += 10;
System.out.println("Vehicle accelerating... " + this.speed + " km/h");
}
}
The Subclass (Child)
Now we create a Car. The Car inherits everything the Vehicle has, and adds its own things (number of doors).
// File: Coche.java (Note: Class name kept as example; ideally translated to Car)
public class Coche extends Vehiculo { // Coche inherits from Vehiculo
private int numberOfDoors;
public Coche(String brand, int doors) {
super(brand); // We call the parent's constructor (See below)
this.numberOfDoors = doors;
}
// Method specific to Coche
public void turnOnAirConditioning() {
System.out.println("Air conditioner on. How cool.");
}
}
By using extends, the Coche class automatically has brand, speed, and the accelerate() method, without having to write them.
Coche myCar = new Coche("Toyota", 5);
myCar.accelerate(); // It works! Inherited from Vehiculo.
myCar.turnOnAirConditioning(); // Specific to Coche.
The super Keyword
When you inherit, sometimes you need to call “dad.” That’s what super is for. It has two main uses:
In the Constructor (super())
When a child (Coche) is born, the parent part (Vehiculo) must be born first.
Java forces you to call the superclass constructor.
- It must be the first line of the child’s constructor.
- If you don’t put it, Java will try to put an invisible
super()(calling the parent’s empty constructor). If the parent has no empty constructor… Compilation error!
Accessing Hidden Methods
If the child and parent have a method with the same name (Overriding, which we’ll see in the next post), you can use super.method() to execute the parent’s original version.
Java and Single Inheritance
Here is a giant difference from C++. In Java, a class can only have ONE parent.
- ✅
public class Coche extends Vehiculo - ❌
public class AmphibiousCar extends Boat, Car(Error)
This was done to avoid the dreaded Diamond Problem (when two parents have the same method, which one does the child inherit?). Java prefers simplicity. If you need behaviors from multiple sources, Java uses Interfaces (we’ll see this soon).
The Object Class: The Parent of All
In Java, there is a universal truth: Everything is an Object.
If you create a class that doesn’t extend anything:
public class Dog { ... }
Java, silently, does this for you:
public class Dog extends Object { ... }
The java.lang.Object class is the root of the entire Java hierarchy. This means that ANY object you create automatically has the methods defined in Object.
The Methods We All Inherit
Thanks to inheriting from Object, any class has:
toString(): Returns a textual representation.Object’s implementation combines the class name with the hash in hexadecimal; it does not show a memory address.equals(Object obj): Compares if two objects are equal. By default, it compares references (==), but we usually override it to compare content (like IDs).hashCode(): A number used by hash-based collections likeHashMap. It doesn’t have to be unique: different objects can produce the same value.getClass(): Tells us what type the object is at runtime.