8 · 32 · 5 min
Two ways to say what something must do
Both let you program against a promise rather than a concrete type, and they answer different questions.
An interface is a contract
It says what an implementer must provide and nothing about how.
public interface PaymentMethod {
Receipt charge(Money amount);
boolean supports(Currency currency);
}A class may implement many interfaces, because implementing several contracts is not contradictory.
An abstract class is a partial implementation
It provides shared code and leaves specific parts to subclasses.
public abstract class Exporter {
public final void export(Report r) { // the shared sequence
validate(r);
write(format(r));
audit(r);
}
protected abstract String format(Report r); // subclasses supply this
}A class may extend only one, because inheriting state from two parents raises the question of which copy you have.
Choosing
| Want | Use |
|---|---|
| A capability several unrelated types can have | interface |
| Shared implementation among related types | abstract class |
| Several of them on one class | interface |
| To hold state | abstract class |
The relationship reading helps: an interface is usually "can do", an abstract class is usually "is a kind of". A Bird and a Drone are not related and can both be Flyable.
Default methods blur the line
Java 8 allowed interfaces to carry implementations:
public interface Collection<E> {
boolean removeIf(Predicate<? super E> filter); // has a body
}The purpose was compatibility: adding a method to an interface would otherwise break every existing implementation. With a default, existing classes keep compiling.
Interfaces still cannot hold instance state, which remains the real difference.
Why interfaces dominate in practice
Two reasons, both practical rather than philosophical.
Testing. A dependency typed as an interface can be substituted with a fake. A dependency typed as a concrete class usually cannot.
Coupling. Depending on an abstraction means the implementation can be replaced without touching the caller, which is the dependency inversion principle stated concretely.
The habit worth forming: type parameters and fields to the interface, not the implementation.
List<String> names = new ArrayList<>(); // swap for LinkedList freely
ArrayList<String> names = new ArrayList<>(); // now the choice is baked in