W3docs

Java Inheritance

Reuse and extend class behavior in Java with the extends keyword and the rules of single inheritance.

Inheritance lets a class build on another instead of starting from scratch. The new class — the subclass — gets all of the parent's fields and methods, and adds (or replaces) only what's different. It's how Java expresses "is-a" relationships: a Cat is an Animal, an AdminUser is a User.

The keyword is extends. The mechanics are straightforward. The hard part — and the part this chapter is really about — is recognizing when inheritance is the right tool and when it isn't.

A first example

public class Animal {
  String name;
  void breathe() { System.out.println(name + " breathes"); }
}

public class Cat extends Animal {
  void purr() { System.out.println(name + " purrs"); }
}

Cat declares only purr(). It still has name and breathe(), because it inherited them:

Cat c = new Cat();
c.name = "Mittens";
c.breathe();     // Mittens breathes — inherited from Animal
c.purr();        // Mittens purrs   — declared on Cat

Animal is the superclass (or parent), Cat is the subclass (or child). Other languages call them base/derived class.

What gets inherited

A subclass inherits:

  • All public and protected fields and methods from every ancestor.
  • Package-private fields and methods, if the subclass is in the same package.
  • All inherited members keep their original modifiers.

A subclass does not inherit:

  • Constructors. They are not members in the same sense — the subclass needs its own.
  • private fields and methods. They exist in the parent (a Cat's memory layout still includes any private Animal fields), but the subclass can't see them by name. The only way to read them is through inherited public/protected accessor methods.

Single inheritance — one parent only

Every class extends exactly one other class. There is no multiple inheritance for classes in Java:

public class Hybrid extends Animal, Vehicle { }    // ERROR — no multiple inheritance

You can implement many interfaces (covered in interfaces) — that gives you the multiple-source flexibility most languages use multiple inheritance for, without the ambiguity headaches.

If you don't write extends at all, the class implicitly extends Object:

public class Foo { }
// is equivalent to
public class Foo extends Object { }

That's why every Java object has toString(), equals(), hashCode(), and getClass() — they're all on Object. The Object class chapter goes through them.

Constructors and super

Every subclass constructor must, as its first action, call a parent constructor. If you don't write the call, Java inserts super() for you:

public class Animal {
  String name;
  public Animal(String name) { this.name = name; }
}

public class Cat extends Animal {
  public Cat(String name) {
    super(name);             // call Animal(String)
  }
}

If Animal had no no-arg constructor and Cat didn't write super(...), the compiler would complain — there's no Animal() to insert implicitly. The super keyword chapter covers super(...) constructor chaining and super.method() calls in full.

Overriding

A subclass can replace an inherited method by declaring one with the same signature:

public class Animal {
  String speak() { return "(some noise)"; }
}

public class Cat extends Animal {
  @Override
  String speak() { return "meow"; }
}

Cat c = new Cat();
System.out.println(c.speak());   // meow

@Override is an annotation that tells the compiler "I intend this method to override an inherited one — please error out if it doesn't." Always use it. It catches typos and signature mismatches that would otherwise silently create a new method instead of overriding the old one. The method overriding chapter covers the full rules.

Upcasting and polymorphism

A subclass instance can be assigned to a variable of the parent type:

Animal a = new Cat();    // upcast — implicit
a.speak();               // calls Cat's speak() — picked at runtime

This is the foundation of polymorphism, the next chapter. The variable a is typed Animal, but the actual object is a Cat, so the Cat version of speak runs.

When inheritance is the wrong tool

Inheritance is the most-overused mechanism in OOP. Some warning signs that you should use composition (a field of another type) instead:

  • The subclass doesn't really pass an "is-a" test. A Stack is not really a Vector — yet java.util.Stack extends Vector and is widely regarded as a design mistake.
  • The parent's mutable internals leak into the subclass. Changes to the parent's implementation break the subclass.
  • You're inheriting to reuse a few methods, not because the types are genuinely substitutable.

The rule of thumb due to Joshua Bloch's Effective Java: prefer composition over inheritance. If B needs A's behavior but isn't really an A, give B a private field of type A and forward what it needs.

// Inheritance — fragile
public class MyList extends ArrayList<String> { ... }

// Composition — robust
public class MyList {
  private final List<String> inner = new ArrayList<>();
  public void add(String s) { inner.add(s); }
}

The composition version is immune to surprises when ArrayList adds new methods or changes how its private fields work.

Inheritance and access

Inherited members keep the modifier they had in the parent. A subclass cannot narrow an overridden method's visibility — making a public method private in the subclass would break the Liskov substitution principle, and the compiler refuses:

public class A {
  public void hello() { }
}
public class B extends A {
  private void hello() { }    // ERROR — cannot reduce visibility
}

You can widen visibility (override a protected method as public), but rarely should.

A worked example

java— editable, runs on the server

What's next

super came up several times here — in constructor chaining and as a way to reach an overridden parent method. The next chapter on the super keyword walks through every situation it shows up in.

Practice

Practice

Which statement about Java inheritance is true?