Interfaces in Java β Contracts, Default and Static Methods
An interface is a pure contract β a set of method signatures any implementing class must provide. Interfaces are Java's answer to multiple inheritance: a class can implement any number of them.
Declaration
public interface Shape {
double area(); // implicitly public abstract
double perimeter();
}
public class Circle implements Shape {
private final double r;
public Circle(double r) { this.r = r; }
@Override public double area() { return Math.PI * r * r; }
@Override public double perimeter() { return 2 * Math.PI * r; }
}
Default methods (Java 8+)
Allow interfaces to evolve without breaking existing implementations:
public interface Shape {
double area();
default boolean isLarge() { return area() > 100; } // concrete
}
Static methods (Java 8+)
public interface Shape {
double area();
static Shape unitCircle() {
return () -> Math.PI;
}
}
var s = Shape.unitCircle();
Private methods (Java 9+)
public interface Logger {
default void info(String msg) { log("INFO", msg); }
default void error(String msg) { log("ERROR", msg); }
private void log(String level, String msg) { // shared helper, not exposed
System.out.println("[" + level + "] " + msg);
}
}
Constants
Every field in an interface is implicitly public static final β i.e. a constant. Declaring constants in an interface is legal but usually an anti-pattern (the implementer "inherits" a namespace pollution). Put constants in a dedicated class.
Functional interfaces
@FunctionalInterface
public interface Mapper<T, R> {
R apply(T input);
}
Mapper<String, Integer> len = s -> s.length();
int n = len.apply("hello"); // 5
A functional interface has exactly one abstract method. Lambdas and method references are their instances.
Marker interfaces
public interface Serializable {} // empty β just a "tag"
public interface Cloneable {}
public interface RandomAccess {} // hint that List.get is O(1)
Marker interfaces are a legacy pattern. Modern code uses annotations for the same purpose (@Documented, @Retention, β¦).
Common mistakes
- Adding a default method in a popular interface β can conflict with existing implementations that already define the same name in a parent class.
- Using interfaces to hold constants β constants pollute implementers' namespace. Use a class with a private constructor.
- Interface with one implementation and no chance of a second β premature abstraction. Add the interface when it's genuinely needed.
Related
Pillar: OOP. Siblings: abstraction, sealed classes, @FunctionalInterface.