In Java's OOP model, when would you choose an abstract class over an interface (or vice versa), and what does each let you do that the other cannot?
answer
- Interface = many (multiple inheritance of type), no instance state
- Abstract class = one (single inheritance), can hold fields + constructors
- Default/static/private methods let interfaces carry behavior (Java 8/9+)
- Deciding factor: need per-instance shared STATE -> abstract class
- Skeletal impl idiom: interface + Abstract... base
basics
~20 sUse an interface to define a pure capability that many unrelated classes can implement, since a class can implement many interfaces. Use an abstract class when you need shared state (fields) or a partial implementation, since a class can extend only one.
solid answer
~50 sBoth abstract classes and interfaces provide abstraction, but they trade off differently. An interface is a contract: it can hold abstract, default, static, and private methods plus constants, but no instance state. Its superpower is that a class can implement many interfaces, giving multiple inheritance of type. An abstract class can have instance fields (shared mutable state), constructors, and any access level on members, but a class can extend only one, so it's single-inheritance. Choose an interface when you're defining a capability that unrelated types should share (Comparable, Runnable), or when you need to keep the door open for multiple inheritance. Choose an abstract class when subclasses share real state or a substantial partial implementation, or when you want a controlled, evolving base class. Since Java 8 default methods, interfaces can also carry behavior, narrowing the gap — but only abstract classes carry per-instance fields, which is usually the deciding factor.
code
java · 20 lines// Capability shared by unrelated types -> interface
interface Drawable {
void draw(); // abstract
default void redraw() { draw(); } // shared behavior, no state
}
// Shared STATE + partial implementation -> abstract class
abstract class Shape implements Drawable {
private final String name; // per-instance state: interfaces can't do this
protected Shape(String name) { this.name = name; } // constructor
public final String name() { return name; }
public abstract double area(); // hook for subclasses
}
class Circle extends Shape {
private final double r;
Circle(double r) { super("circle"); this.r = r; }
public double area() { return Math.PI * r * r; }
public void draw() { /* ... */ }
}go deeper
Knows a class implements many interfaces but extends one class, and that abstract classes can have method bodies and fields.
Articulates the capability table (state, constructors, multiple inheritance) and picks correctly for a given scenario.
Reasons about default methods narrowing the gap, the skeletal-implementation idiom, and API evolution/compatibility when adding methods.
Weighs long-term API evolvability (binary compatibility of adding interface default methods vs abstract methods), module boundaries, and Dependency Inversion across a large codebase.
## The shared goal: abstraction Both tools let you program against an **abstraction** — a type that says *what* operations exist without forcing a single *how*. The choice between them is one of Java's most common design questions, and it hinges on a few concrete capability differences. ## What an interface is An **interface** declares a *contract* — a set of method signatures a class promises to fulfill via `implements`. Its members: - **abstract methods** — implicitly `public abstract` (no body); implementers must provide them. - **constants** — any field is implicitly `public static final`. **No instance state.** - **`default` methods** (Java 8+) — methods *with* a body that implementers inherit and may override. - **`static` methods** (Java 8+) — utility methods called on the interface itself. - **`private` / `private static` methods** (Java 9+) — internal helpers shared by default methods. The defining property: a class can `implements` **many** interfaces — **multiple inheritance of type**. ## What an abstract class is An **abstract class** (`abstract class`) is a class that cannot be instantiated and may declare **abstract methods** (no body) alongside concrete ones. Crucially it can also have: - **instance fields** — real per-object **state** shared by all subclasses. - **constructors** — run via `super(...)` to initialize that state. - **members of any access level** (`private`, `protected`, package-private, `public`). The defining constraint: a class can `extends` **only one** class — **single inheritance of implementation**. ## The capability comparison | Capability | Interface | Abstract class | |---|---|---| | Multiple inheritance | yes (implement many) | no (extend one) | | Instance fields / mutable state | **no** | **yes** | | Constructors | no | yes | | Method bodies | default/static/private | yes (any) | | Member access levels | public-ish only | any | | Constants | yes (public static final) | yes | ## Decision guide **Prefer an interface when:** - you're defining a **capability/role** that *unrelated* classes should share (e.g. `Comparable`, `Serializable`, `Runnable`) — a `String` and a `LocalDate` are both `Comparable` but share no base class; - you need to keep classes free to extend something else (preserve their one `extends` slot); - you want maximum decoupling — depend on interfaces, not concrete bases (Dependency Inversion). **Prefer an abstract class when:** - subclasses genuinely share **state** (fields) or a substantial **partial implementation** you don't want to duplicate; - you want a **controlled, evolving base** with non-public helpers and constructor logic; - you're encoding a **Template Method** skeleton: concrete algorithm steps in the base, abstract "hook" methods for subclasses (`AbstractList` is the JDK example). ## How default methods blurred the line Before Java 8, the rule of thumb was simple: interfaces = pure contract, abstract classes = shared code. **Default methods** let interfaces ship behavior, so an interface can now provide reusable implementations too. But two differences remain decisive: 1. **Interfaces still cannot hold instance fields** — no per-object state. If subtypes need shared mutable state, you need an abstract class. 2. **A class can implement many interfaces but extend one class** — so an interface is the safer default for a public capability. A common modern pattern combines both: a public **interface** for the contract, plus an optional **abstract skeletal implementation** (`Abstract...`) that provides the boilerplate via default-method-friendly hooks — Effective Java's *skeletal implementation* idiom. ## Rule of thumb Default to an **interface** for the type/contract; reach for an **abstract class** only when you must share state or significant implementation, or when you want to lock down a controlled hierarchy.
- Since Java 8 default methods let interfaces carry behavior, why not always use interfaces?Interfaces still cannot declare instance fields, so they can't hold per-object state or constructor logic. When subtypes must share mutable state or substantial initialization, an abstract class is required. Interfaces remain preferable when no shared state is needed.
- What is the skeletal implementation idiom and why is it useful?Define the type as an interface, then provide an abstract 'AbstractXxx' class that implements the tedious boilerplate via the interface's primitive methods. Implementers get the interface's flexibility plus optional free boilerplate by extending the skeleton — best of both. AbstractList is the canonical JDK example.
An interface is a job certification ('licensed electrician') — you can hold many at once and they only specify what you can do. An abstract class is a half-built house you must finish — there's only one foundation you can build on, but it comes with walls already up.
saying these in an interview costs you the question
- Claiming interfaces can have instance fields — they only have public static final constants.
- Saying a class can extend multiple abstract classes — single inheritance allows only one.
- Treating default methods as making abstract classes obsolete — state and constructors still require a class.
- Defaulting to abstract classes for public types, locking implementers out of their single extends slot.