Abstract Classes and Interfaces

Learning goals

Choose between abstract classes and interfaces, implement contracts with implements, use default and static interface methods, and design APIs that stay flexible as requirements evolve.


Abstract classes

Cannot be instantiated; may mix abstract and concrete members:

public abstract class Payment {
    protected final String id;

    protected Payment(String id) {
        this.id = id;
    }

    public abstract void capture();  // subclass must implement

    public void log() {
        System.out.println("Payment " + id);
    }
}

public class CardPayment extends Payment {
    public CardPayment(String id) {
        super(id);
    }

    @Override
    public void capture() {
        System.out.println("Charging card " + id);
    }
}

Use abstract classes when subclasses share state and implementation (protected fields, concrete helpers).


Interfaces

Pure contract—methods without implementation (historically):

public interface Notifier {
    void send(String message);
}

public class SmsNotifier implements Notifier {
    @Override
    public void send(String message) {
        System.out.println("SMS: " + message);
    }
}

A class extends one superclass, implements zero or more interfaces:

public class EmailSmsNotifier implements Notifier, Auditable {
    // implement both interfaces
}

Default and static methods (JDK 8+)

Evolve interfaces without breaking implementors:

public interface Notifier {
    void send(String message);

    default void sendUrgent(String message) {
        send("[URGENT] " + message);
    }

    static Notifier noop() {
        return message -> { };
    }
}

Default — instance method with body; inherited unless overridden.

Static — called on interface (Notifier.noop()), not on instance.


Private interface methods (JDK 9+)

Share code between defaults without exposing helpers:

public interface Parser {
    default int parseInt(String s) {
        return Integer.parseInt(trim(s));
    }

    private String trim(String s) {
        return s.strip();
    }
}

Abstract class vs interface

Abstract class Interface
Inheritance Single extends Multiple implements
State Can hold fields Only public static final constants traditionally; records/sealed patterns evolve
Constructor Yes No instance fields to construct
Use when Shared base behavior + state Capability / role (“Can fly”, “Serializable”)

Modern style: prefer interfaces for API boundaries; abstract class when template method pattern needs shared non-public state.


Functional interfaces (JDK 8+)

Single abstract method (SAM) — lambda target:

@FunctionalInterface
public interface Transformer<T, R> {
    R apply(T input);
}

Transformer<String, Integer> len = s -> s.length();
System.out.println(len.apply("hi"));  // 2

@FunctionalInterface is optional but documents intent; compiler enforces one abstract method.


Template method pattern

Abstract class defines skeleton; subclasses fill steps:

public abstract class DataImporter {
    public final void importAll() {  // final prevents override of flow
        open();
        read();
        close();
    }

    protected abstract void read();
    protected void open() { System.out.println("Opening"); }
    protected void close() { System.out.println("Closing"); }
}

Marker interfaces (legacy)

Empty interface (Serializable) tags capability. Prefer annotations or explicit methods today unless interfacing with JDK APIs that require markers.


Common mistakes

  1. Implementing interface but wrong method signature — not an override; compile error if @Override used.
  2. Diamond conflict — two interfaces with same default method; class must override to resolve.
  3. Fat interfaces — violate Interface Segregation; split Worker into Runnable, Stoppable.
  4. Abstract class for only constants — use interface or final class with private constructor.
  5. Public fields in interfaces — constants are implicitly public static final; don’t misuse as data bag.

Practice checkpoint

  1. Define Drawable with void draw() and implement in Circle.

    • Answer: interface + @Override void draw().
  2. Can a class extend Payment and implement Auditable?

    • Answer: Yes—one extends, many implements.
  3. What happens if two interfaces define same default method?

    • Answer: Implementing class must override unless one inherits from the other.
  4. When use abstract class over interface for Animal/Dog?

    • Answer: Shared fields (name, age) and partial implementation in base.

Interview angles

  • “Interface vs abstract class JDK 8+?” — Interfaces can have defaults/static; still single inheritance for classes.
  • “Why default methods?” — Backward-compatible library evolution (e.g., Collection.stream()).
  • “Can interface have constructor?” — No.

Next: /java/learn/oop/packages-and-access/