Lesson 1 of 518 minModule progress 0%

Module 8: Object-Oriented Design in Practice

Abstract Classes and Interfaces

Learn how to choose between a shared base class and a shared contract without over-designing your class model.

Author

Java Learner Editorial Team

Reviewer

Technical review by Java Learner

Last reviewed

2026-04-17

Java version

Java 25 LTS

How this lesson was prepared: AI-assisted draft, manually edited for clarity, and checked against current Java documentation and runnable examples.

Learning goals

  • Know when an abstract class is the better fit
  • Know when an interface keeps designs flexible
  • Avoid mixing both without a clear reason

Before you start

  • You are comfortable creating classes, objects, methods, and constructors

Why this matters: Intermediate Java is where design starts to matter more than syntax. The wrong abstraction can lock you into brittle hierarchies fast.

Use an abstract class when subclasses share real code or state: An abstract class is helpful when several related types need common fields, helper methods, or a shared workflow.

Use an interface when you want a capability, not a family tree: Interfaces are better when unrelated classes need the same contract, such as Payable, Searchable, or Exportable.

Good rule: If your first thought is shared state, lean abstract class. If your first thought is shared behavior from different kinds of objects, lean interface.

Runnable examples

One shared base class plus one capability interface

abstract class Employee {
    private final String name;

    protected Employee(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public abstract double monthlyPay();
}

interface Reportable {
    String buildReport();
}

class Developer extends Employee implements Reportable {
    private final double salary;

    Developer(String name, double salary) {
        super(name);
        this.salary = salary;
    }

    @Override
    public double monthlyPay() {
        return salary;
    }

    @Override
    public String buildReport() {
        return getName() + " ships code.";
    }
}

Expected output

Developer objects share Employee state and also satisfy the Reportable contract.

Common mistakes

Using inheritance just to share one helper method

Prefer composition or a utility/helper class when there is no true shared abstraction.

Putting lots of unrelated default behavior in one interface

Keep interfaces small and capability-focused so implementations stay clear.

Mini exercise

Create an abstract `Shape` class with an abstract `area()` method, then add a `Drawable` interface with a `draw()` method.

Summary

  • Abstract classes are best for shared state or shared implementation.
  • Interfaces define capabilities across unrelated types.
  • Good design starts with clear intent, not with trying to reuse every line of code.

Next step

Next, compare composition against inheritance so you can spot when subclassing is the wrong move.

Sources used

Advertisement

Lesson check

When is an interface usually the better choice?

Next lesson →