Java Interfaces
›Short versionOpen if you only have a minute.
- An interface names methods. A class implements it. Callers can depend on the interface and never name the class.
- Interface fields are public static final constants. The interface does not store per-instance state.
- A class extends one class and can implement many interfaces. That is how Java shares a capability across unrelated types.
- A default method ships a body on the interface. A class inherits it and may override it. Two competing defaults must be overridden.
- Static methods on an interface are called on the interface name. They are a place for factories, not for object behavior.
- Use an abstract class when the family shares fields or a constructor. Use an interface when the shared thing is a contract.
A test needs a clock that always says noon. Production needs the operating system clock. Both have to answer one question: what instant is it now? The is that question, written as a type, so the report code can ask without knowing which clock it holds.
The promise and the classes that keep it
The is small on purpose. One method, a clear return, no setup the caller must remember.
import java.time.Instant;
public interface Clock {
Instant instant();
default boolean isAfter(Instant then) {
return instant().isAfter(then);
}
static Clock fixed(Instant instant) {
return () -> instant;
}
}isAfter is a . It is written once, from instant(), and every implementation gets it. fixed is a . You call Clock.fixed(noon). There is no clock object yet. The method returns one. The lambda works because Clock has a single abstract method.
public final class SystemClock implements Clock {
@Override
public Instant instant() {
return Instant.now();
}
}
public void report(Clock clock) {
System.out.println(clock.instant());
}implements is the clause.report takes a Clock. A SystemClock and the object from Clock.fixed both qualify. Overriding instant is dynamic dispatch, the same rule as a superclass method. Adding instant(ZoneId) on one class would be an overload, chosen by the compiler from the arguments, not a second way to satisfy the interface.
Many contracts, one parent class
A card payment may already extend an abstract Payment that holds the payment id. It can still Clock only if it is a clock, which it is not. It can implement Retryable and Audited because those are capabilities, not a second parent in the payment family.
// Payment.java
public abstract class Payment {
private final String id;
protected Payment(String id) {
this.id = id;
}
public String id() {
return id;
}
}
// CardPayment.java
public final class CardPayment extends Payment implements Retryable, Audited {
public CardPayment(String id) {
super(id);
}
@Override
public boolean retry() {
return true;
}
@Override
public String auditId() {
return id();
}
}That split is the practical difference from an . The abstract class spends the one extends and can store id in a field. The interfaces add roles without storing a second copy of that id. If both Retryable and Audited declared default String label(),CardPayment would have to override label and say which body it wants.
Callers should program to the interface when the interface is the contract they mean. Naming SystemClock in a report method welds the report to the operating system and makes the noon clock impossible to pass. Name the class at the place that builds the object. Pass the interface after that.
$ javac Clock.java SystemClock.java$ java Report
Two types need a shared id field and a constructor check, and a third unrelated type needs only the retry() method. Where does the id go?
The id goes on an abstract class the first two types extend. retry() goes on an interface all three can implement. The interface cannot hold a different id on each instance. The unrelated type implements Retryable and keeps whatever superclass it already has.