StackWise
Modern JavaFundamentals

Interfaces vs Abstract Classes

Default, static and private interface methods, the diamond problem, functional interfaces — and how to choose between interface and abstract class.

beginner 3 min readLesson 3 of 26

Interfaces

An interface is a contract: a set of method signatures a class promises to implement. Since Java 8 interfaces can also carry behaviour:

Member Since Notes
Abstract methods 1.0 Implicitly public abstract
Constants 1.0 Implicitly public static final
default methods 8 Implementation inherited by implementors; can be overridden
static methods 8 Called as Interface.method(); not inherited
private methods 9 Share code between default methods
public interface Shape {
    double area();                                   // abstract

    default String describe() {                      // default
        return "%s with area %.2f".formatted(name(), area());
    }

    static Shape unitSquare() { return () -> 1.0; }  // static factory

    private String name() {                          // private helper
        return getClass().getSimpleName();
    }
}

Abstract classes

An abstract class is a partially implemented class that can’t be instantiated. Unlike interfaces, it can have instance state, constructors, and methods with any access level.

public abstract class HttpHandler {
    private final MeterRegistry metrics;            // state
    protected HttpHandler(MeterRegistry m) { this.metrics = m; }

    public final Response handle(Request r) {       // template method
        metrics.counter("requests").increment();
        return doHandle(r);
    }
    protected abstract Response doHandle(Request r);
}

How to choose

Interface Abstract class
Multiple inheritance A class can implement many A class can extend only one
State (instance fields) No Yes
Constructors No Yes
Method access public (or private helpers) Any
Relationship can-do (capability) is-a (shared identity)
Evolving the API Add default methods Add concrete methods

The diamond problem

If a class inherits the same default method from two interfaces, the compiler forces it to resolve the conflict explicitly:

Diamond.javaJava 21
interface Camera {
    default String capture() { return "photo"; }
}

interface Phone {
    default String capture() { return "voice memo"; }
}

class Smartphone implements Camera, Phone {
    // Without this override: "class Smartphone inherits unrelated defaults for capture()"
    @Override
    public String capture() {
        return Camera.super.capture() + " + " + Phone.super.capture();
    }
}

abstract class Device {
    public String capture() { return "device capture"; }
}

class ActionCam extends Device implements Camera {
    // No conflict: a class method always wins over an interface default ("class wins" rule)
}

class Main {
    public static void main(String[] args) {
        System.out.println(new Smartphone().capture());
        System.out.println(new ActionCam().capture());
    }
}

The resolution rules:

  1. Classes win — a method in the superclass chain takes priority over any interface default.
  2. More specific interfaces win — if B extends A and both define the default, B’s is used.
  3. Otherwise the class must override and may delegate with X.super.method().

Functional interfaces

An interface with exactly one abstract method is a functional interface and can be implemented with a lambda. @FunctionalInterface makes the compiler enforce the single-abstract-method rule.

@FunctionalInterface
interface Validator<T> {
    boolean validate(T value);

    default Validator<T> and(Validator<T> other) {        // defaults don't count
        return v -> validate(v) && other.validate(v);
    }
}

Validator<String> notBlank = s -> !s.isBlank();
Validator<String> shortEnough = s -> s.length() <= 20;
boolean ok = notBlank.and(shortEnough).validate("Ada");

See Lambdas & Functional Interfaces for the built-in ones (Function, Predicate, Supplier, Consumer…).

Sealed interfaces

Since Java 17 you can restrict which classes may implement an interface with sealed … permits. This makes hierarchies closed and enables exhaustive pattern matching — see Sealed Classes & Pattern Matching.

public sealed interface Payment permits Card, BankTransfer, Wallet {}

Marker interfaces

Interfaces with no methods — Serializable, Cloneable, RandomAccess — tag a class with a capability checked at runtime via instanceof. Modern code often uses annotations instead, but marker interfaces have the advantage of being checked by the type system.

Finished this lesson?

Mark it complete to track your progress through Modern Java.