Week 3 Day 2 — Interfaces vs Abstract Classes
Goal
Today I want a decision rule: interface for a capability, abstract class for a shared implementation skeleton.
Main questions:
- What can an interface hold?
- What can an abstract class hold?
- When do I pick which?
- What are default methods for?
- Why does Spring Data use interfaces for repositories?
1. Interface
An interface is a type that describes what an object can do.
public interface OrderRepository {
Order save(Order order);
Optional<Order> findById(long id);
}
A class can implement many interfaces. That is Java’s way to have multiple types without multiple class inheritance.
Modern interfaces may contain:
- abstract methods (the usual)
defaultmethods with a bodystaticmethodsprivatemethods (helpers for defaults)- constants (
public static finalfields)
They cannot hold instance fields. They cannot remember per-object state unless the implementing class does.
Memory sentence:
An interface is a capability. It does not store instance state.
2. Abstract class
An abstract class is a partial class. It can have fields, constructors, implemented methods, and abstract methods.
public abstract class Payment {
private final Money amount;
protected Payment(Money amount) {
this.amount = amount;
}
public final Money amount() {
return amount;
}
public abstract void settle();
}
A class can extend only one abstract class. I use that when implementations truly share state and a template.
abstract on a class means I cannot new it. abstract on a method means subclasses must implement it (unless they are also abstract).
3. Decision rule
| I need | I use |
|---|---|
| A seam for tests and extra implementations | interface |
Several independent capabilities (Closeable, Comparable) | interface |
| Shared fields and a constructor invariant | abstract class |
| A template method that calls abstract steps | abstract class |
| JDK proxy / Spring Data repository | interface |
If I am unsure, I start with an interface. I extract an abstract class later only when two implementations copy real state or a real algorithm.
A class can do both: class StripePayment extends Payment implements Refundable.
4. Default methods
default methods let me add a method to an interface without breaking existing implementors.
public interface OrderRepository {
Order save(Order order);
default Order saveAndFlush(Order order) {
return save(order);
}
}
If two interfaces provide the same default method, the class must override and pick (or call InterfaceName.super.method()).
Default methods are not a place to hide business services. They have no instance fields. Heavy defaults become an abstract class or a collaborator.
Functional interfaces (one abstract method) are the target types for lambdas: Function, Predicate, Converter. Spring uses them all over (Converter<S,T>, ApplicationListener).
5. Spring connection
Repositories:
public interface OrderRepository extends JpaRepository<Order, Long> {}
I write an interface. Spring Data creates a proxy that implements it. My service depends on the interface. That is polymorphism plus a generated implementation.
Services: I often still write a concrete class without an interface when there is one implementation and I test with the constructor. I add an interface when I have two implementations, a module boundary, or I want a JDK proxy.
JDK vs CGLIB proxies:
- JDK proxy: requires an interface. The proxy implements it, does not extend the class.
- CGLIB: subclasses the concrete class. Needs a non-final class and a non-private constructor.
If the injection type is the class, I get a CGLIB subclass (in typical Spring setup). If the injection type is the interface, I can get a JDK proxy. Self-invocation problems exist in both cases because this is not the proxy.
6. Common traps
Trap 1: Abstract class “just in case we reuse fields.”
Empty abstract classes are a tax. Wait for real sharing.
Trap 2: Fat interface (OrderRepository that also sends email).
Implementors fake half the methods. Split types (Interface Segregation, tomorrow).
Trap 3: Default method that needs state.
Interfaces cannot hold it. The default will cheat with statics or throw.
Trap 4: “Spring requires an interface for every service.”
It does not. Interfaces are for substitution and some proxy modes. One concrete @Service is fine.
Practice Questions and Answers
Question 1
Interface vs abstract class — when each?
Answer:
Interface for a capability and a test seam, especially when a class needs several types. Abstract class when implementations share fields, a constructor, or a template algorithm. I start with an interface unless I already have shared state.
Question 2
Why can a class implement many interfaces but extend only one class?
Answer:
Java has single class inheritance to avoid the diamond problem on state. Interfaces add types without instance fields, so multiple interfaces are allowed. Default methods can still conflict; the class must resolve that.
Question 3
What is a default method for?
Answer:
To evolve an interface with a method that has a reasonable implementation, without breaking existing implementors. Not for storing state or growing a hidden service.
Question 4
Why are Spring Data repositories interfaces?
Answer:
The application describes the capability (save, findById, query methods). Spring Data supplies a proxy implementation at runtime. My services depend on the interface, so tests can pass a fake.
Question 5
Do I need an interface for every Spring @Service?
Answer:
No. I add one when I need a second implementation, a module API, or a JDK proxy. A single concrete service with constructor injection is a valid design.
Memory sentences
Interface = capability. Abstract class = shared skeleton with state.
Start with an interface. Extract an abstract class when state is really shared.
Spring Data repositories are interfaces so the container can supply the implementation.