skip to content

When would you choose an interface over an abstract class for abstraction in Java?

level: seniorimportance: should knowfreq 72%

answer

  1. Interface = capability across unrelated types; multiple implements
  2. Abstract class = shared state + constructors + one hierarchy
  3. Java 8 defaults removed the 'no bodies' tiebreaker
  4. Remaining differentiators: state, constructors, non-public members
  5. Effective Java: interface + skeletal abstract class

basics

~20 s

Use an interface to define a capability that unrelated types can share, since a class can implement many interfaces. Use an abstract class when you need shared state, constructors, or partial implementation across closely related types.

solid answer

~50 s

Both provide abstraction, but they trade off differently. Choose an **interface** when you want to define a *capability* or *role* that unrelated classes can adopt — a type can implement many interfaces, so they compose freely (mixins, capability APIs). Interfaces hold no instance state and no constructors, only constants, abstract methods, and `default`/`static` methods. Choose an **abstract class** when implementations are closely related and need to share *state* (instance fields), *constructors*, or a substantial *partial implementation* / template-method skeleton — but a class can extend only one, so it forces a single hierarchy. Since Java 8 the gap narrowed: default methods let interfaces ship behavior, so 'interfaces can't have bodies' is no longer a reason to pick an abstract class. The modern default is: prefer interfaces for the public type, optionally pair with a 'skeletal' abstract class (the `AbstractList` pattern) that implementors can extend for convenience.

go deeper

for a junior

States that a class implements many interfaces but extends one abstract class; knows abstract classes can have constructors.

for a middle

Lists the concrete differences (state, constructors, multiple inheritance) and gives an example for each.

for a senior

Articulates the decision rubric, the Java-8 default-method shift, and the interface + skeletal-class pattern.

for a principal

Drives API/library design choices around evolution, binary compatibility, and composability; defaults to interfaces with skeletal helpers.

## The two tools **Abstraction** means exposing *what* a type does while hiding *how*. Java offers two mechanisms: - An **abstract class** — a class declared `abstract` that **cannot be instantiated** and may contain abstract methods (no body) alongside concrete methods, instance fields, and constructors. Subclasses `extends` it and fill in the abstract methods. - An **interface** — a pure contract: abstract methods, constants (`public static final`), and since Java 8 `default`/`static` method bodies, but **no instance state and no constructors**. Classes `implements` it. ## What each can do | Feature | Interface | Abstract class | |---|---|---| | Multiple inheritance | yes (implement many) | no (extend one) | | Instance fields (state) | no (constants only) | yes | | Constructors | no | yes | | Method bodies | `default`/`static`/`private` (Java 8/9+) | yes, freely | | Access modifiers on members | mostly public | any (`protected`, `private`, etc.) | | Can hold mutable per-object state | no | yes | ## Decision guide **Pick an interface when:** 1. **Unrelated types share a capability.** `Comparable`, `Serializable`, `AutoCloseable` cut across class hierarchies. A class can implement many of them; it can extend only one class. 2. **You want composable roles / mixins.** Small interfaces combine to describe a type's capabilities. 3. **You're defining a public API surface.** Interfaces decouple callers from implementations, enabling DI, mocking, and multiple implementations. 4. **There is no shared state to inherit** — only behavior contracts. **Pick an abstract class when:** 1. **Closely related types share state.** Instance fields (e.g. a cached value, a name) must live somewhere; interfaces can't hold them. 2. **You need constructors / construction invariants** enforced for all subtypes. 3. **You want a substantial partial implementation** with `protected` helpers and the **template method pattern** (a concrete method that calls abstract steps subclasses fill in). 4. **You want non-public members** (interfaces are essentially public). ## The 'both' pattern: interface + skeletal class The canonical Java idiom (Effective Java) is to **prefer the interface for the type** and additionally provide a **skeletal implementation** abstract class for convenience — the `AbstractList`/`AbstractMap` pattern. Implementors get the flexibility of the interface and, if they want, can extend the skeleton to avoid boilerplate. This gives you the best of both. ## How Java 8 changed the calculus Before Java 8, a reason to choose an abstract class was 'I need to ship shared behavior, and interfaces can't have bodies.' **Default methods removed that reason** — an interface can now provide behavior and even evolve (add a method) without breaking existing implementors, as long as the new method has a default. So today the remaining hard differentiators are **state, constructors, and non-public members**, which only abstract classes provide. ## Rule of thumb Default to an interface for the *type*. Reach for an abstract class only when you genuinely need shared mutable state, a constructor contract, or a rich protected template — and even then, often expose an interface and back it with a skeletal abstract class.

  • Since Java 8 interfaces can have default methods, why use an abstract class at all?
    Abstract classes can hold instance state (mutable fields), define constructors and construction invariants, declare protected/private members, and provide a rich template-method skeleton. Interfaces still cannot hold per-instance state or constructors.
  • What is the skeletal implementation (AbstractList) pattern?
    Define the type as an interface, then provide an abstract class implementing most of it with protected helpers, so concrete classes can either implement the interface directly or extend the skeleton to skip boilerplate — combining interface flexibility with code reuse.

saying these in an interview costs you the question

  • Saying 'use abstract class when you need method bodies' (defaults provide those since Java 8)
  • Claiming interfaces can hold instance state
  • Treating the two as interchangeable with no trade-offs
  • Forcing unrelated types into one abstract-class hierarchy just to share a method

context