Inheritance in Java

Inheritance lets a subclass acquire the members of a superclass, creating an is-a relationship that polymorphism is built on.

Definition

Inheritance allows one class, the subclass, to acquire the accessible fields and methods of another, the superclass. The subclass may add members of its own and may replace inherited behaviour.

class Subclass extends Superclass { }

Why it exists

  • Behaviour shared by several types is written once.
  • It establishes an is-a relationship the compiler can check.
  • It makes runtime polymorphism possible, which is the real payoff.

Example

class Vehicle {

    private final String registration;

    Vehicle(String registration) {
        this.registration = registration;
    }

    String registration() {
        return registration;
    }

    String describe() {
        return "Vehicle " + registration;
    }
}

class Taxi extends Vehicle {

    private final int seats;

    Taxi(String registration, int seats) {
        super(registration);       // the superclass part is built first
        this.seats = seats;
    }

    @Override
    String describe() {
        return "Taxi " + registration() + " with " + seats + " seats";
    }
}
Vehicle v = new Taxi("TN-09-4471", 4);
System.out.println(v.describe());   // Taxi TN-09-4471 with 4 seats

The variable is declared as Vehicle, but the object is a Taxi, so the override runs. That is polymorphism, and inheritance is what enables it.

What is and is not inherited

MemberInherited
public and protected membersYes
Package private membersOnly within the same package
private membersNo, though they still exist in the object
ConstructorsNo
Static membersAccessible, but hidden rather than overridden
A private field is not inherited, but it is still part of the object in memory. The subclass simply cannot name it, and must go through an accessible method instead.

Types of inheritance in Java

TypeSupportedNote
SingleYesOne subclass extends one superclass.
MultilevelYesA subclass is itself extended.
HierarchicalYesSeveral subclasses share one superclass.
Multiple, using classesNoA class extends exactly one class.
Multiple, using interfacesYesA class may implement many interfaces.

Why multiple class inheritance was left out

If two superclasses both provided a save() method, the compiler could not decide which one an instance inherits. That ambiguity is often called the diamond problem. Interfaces avoid it because, historically, they carried no implementation. Since Java 8 interfaces may have default methods, so the language added an explicit rule: when two interfaces supply the same default method, the implementing class must override it and may choose one with InterfaceName.super.method().

interface Printable { default String format() { return "printable"; } }
interface Exportable { default String format() { return "exportable"; } }

class Report implements Printable, Exportable {
    @Override
    public String format() {
        return Printable.super.format();   // the ambiguity must be resolved
    }
}

Constructors and the chain

A subclass constructor always runs a superclass constructor first, whether or not super(...) is written. If the superclass has no no argument constructor and the subclass does not call one explicitly, the code does not compile.

new Taxi(...)
    -> Object()
    -> Vehicle(registration)
    -> Taxi(registration, seats)

Every class inherits from Object

A class with no extends clause implicitly extends Object. That is where toString, equals, hashCode and getClass come from, and why they are available on every reference.

Preventing inheritance

public final class TaxCalculator { }      // cannot be extended

public class Base {
    public final void audit() { }         // cannot be overridden
}

Since Java 17, sealed offers a middle position: the superclass names exactly which classes may extend it.

public sealed class Payment permits CardPayment, UpiPayment { }

Common mistakes

  • Using inheritance for code reuse when there is no is-a relationship. A Stack is not a Vector, and treating it as one exposes operations that break it.
  • Building deep hierarchies. Beyond two or three levels, behaviour becomes hard to trace.
  • Making fields protected, which locks the representation for every future subclass.
  • Calling an overridable method from a constructor, so the override runs before the subclass fields exist.
  • Expecting a private method to be overridden. It is not inherited, so a subclass method of the same name is unrelated.

Best practices

  • Ask whether the subtype can be substituted anywhere the supertype is expected. If not, the relationship is wrong.
  • Prefer composition unless inheritance is clearly the better model.
  • Design for inheritance deliberately, or make the class final.
  • Document what a subclass may override and what it must not.
  • Keep superclass fields private and expose protected accessors only when needed.

Practice

  1. Model Employee, Manager and Intern and decide which methods belong where.
  2. Why does class Child extends Parent { Child() { } } fail when Parent only has Parent(String)?
  3. Two interfaces provide the same default method. Write the class that resolves it.
  4. Explain why a Square extends Rectangle with a setWidth method is a design problem.
  5. Convert a two level hierarchy into composition and compare the two designs.

Conclusion

Inheritance expresses is-a and unlocks polymorphism. Use it when the subtype can honestly stand in for the supertype, keep hierarchies shallow, and prefer composition whenever the relationship is really has-a.

Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All Java notes →

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.