← The curriculum
§ 2.02 · Object-Oriented Java

Interfaces, abstract classes, inheritance

Polymorphism done right — when to extend, when to implement, and why composition usually wins.

Intermediate · 28 minute read
Try it yourself
Live Java · editable

Runs on a sandboxed JVM via the public Piston API. Edit the code and hit Run.

§ 2.02.01

Interfaces describe capability

An interface is a contract: any class that implements it must provide the listed methods. A class can implement many interfaces but extend only one class. Prefer interfaces at the boundary of your design — they're easier to mock, easier to extend, and free of the fragile-base-class problem.

§ 2.02.02

Default methods

Since Java 8, interfaces can carry default method implementations. This lets you add methods without breaking every existing implementer. Use it sparingly — an interface's job is to declare a contract, not to hold logic.

§ 2.02.03

Abstract classes

An abstract class can have state, constructors, and a mix of concrete and abstract methods. Reach for one when several subclasses genuinely share both fields and behavior. In practice, most cases are better served by an interface plus a helper class you compose in.

§ 2.02.04

Inheritance: `extends`

class Manager extends Employee says a Manager is-an Employee. The subclass inherits fields and methods, can override them with @Override, and can extend the parent constructor with super(...). Inheritance is a strong coupling — every parent change ripples through every child. Use it only when the is-a relationship is genuine and stable.

§ 2.02.05

Favor composition over inheritance

Instead of class Cache extends HashMap, write class Cache { private final Map<K,V> store = new HashMap<>(); }. Composition lets you expose only the methods you want, swap implementations, and avoid inheriting bugs. The Joshua Bloch rule — prefer composition — is one of the most reliable design guidelines in Java.

§ 2.02.06

Sealed types

Since Java 17, sealed interface Shape permits Circle, Square limits who can implement the interface. Combined with pattern-matching switch, this gives you exhaustive checks — the compiler catches new subtypes you forgot to handle.

Example
Live Java · editable

Runs on a sandboxed JVM via the public Piston API. Edit the code and hit Run.