Design flexible contracts with interfaces and share logic with abstract classes.
Published January 17, 2025
Both define contracts that subclasses/implementors must fulfil — but they serve different purposes.
public interface Payable {
void pay(double amount); // abstract (must implement)
String getReceipt(); // abstract
default String currency() { // default method — optional override
return "USD";
}
}
publicstatic final constants)default methods allow behaviour in interfacespublic abstract class Shape {
private String color; // instance state — allowed!
public Shape(String color) { this.color = color; }
public abstract double area(); // subclasses must implement
public String describe() { // shared behaviour
return color + " shape with area " + area();
}
}
public class Circle extends Shape {
private double radius;
public Circle(String color, double radius) { super(color); this.radius = radius; }
@Override
public double area() { return Math.PI * radius * radius; }
}
| Interface | Abstract Class | |
|---|---|---|
| State | No | Yes |
| Multiple inheritance | Yes | No (single) |
| Constructor | No | Yes |
| Use when | Defining a capability | Sharing implementation |
"Favour interfaces over abstract classes when you only need to define a contract. Abstract classes shine when you want to share code among closely related classes."
In Java 8+, interfaces are more powerful than ever (default methods). The distinction has blurred, but abstract classes still win when you need shared mutable state.