
What does implements mean in Java?
An interface declares capabilities (method signatures, default methods, static methods, constants). A class uses implements to promise it provides those capabilities.
public interface Cache {
Optional<String> get(String key);
void put(String key, String value);
default boolean contains(String key) { return get(key).isPresent(); }
}
public class InMemoryCache implements Cache {
private final Map<String, String> store = new ConcurrentHashMap<>();
public Optional<String> get(String key) { return Optional.ofNullable(store.get(key)); }
public void put(String key, String value) { store.put(key, value); }
}
Key points:
- A class can implement multiple interfaces:
class A implements I1, I2 { ... }. - Interfaces can have
defaultmethods (since Java 8), helping evolve APIs without breaking implementors. - Records and enums can implement interfaces.
- Interfaces cannot hold state (beyond
public static finalconstants).
What does extends mean in Java?
1) Class → Class (single inheritance)
A class can extend one other class to reuse state/behavior and specialize it.
public abstract class Shape {
public abstract double area();
}
public class Rectangle extends Shape {
private final double w, h;
public Rectangle(double w, double h) { this.w = w; this.h = h; }
@Override public double area() { return w * h; }
}
2) Interface → Interface (multiple inheritance of type)
An interface can extend one or more interfaces to combine contracts.
public interface Startable { void start(); }
public interface Stoppable { void stop(); }
public interface Lifecycle extends Startable, Stoppable { }
Notes:
- Classes cannot extend multiple classes.
- Use
super(...)to call a superclass constructor; use@Overrideto refine behavior. - If two parent interfaces provide the same
defaultmethod, the implementing class must disambiguate by overriding.
Differences at a glance
| Topic | implements | extends (class→class) | extends (interface→interface) |
|---|---|---|---|
| Purpose | Promise behavior via interface | Reuse/ specialize implementation & state | Combine contracts |
| Multiple inheritance | Class can implement many interfaces | Not allowed (single superclass) | Allowed (an interface can extend many) |
| State | No instance state in interface | Inherits fields and methods | No instance state |
| Constructors | N/A | Subclass calls super(...) | N/A |
| API evolution | default methods help | Risky; changes can ripple | default in parents propagate |
| Typical use | Plug-in points, ports, test seams | True specialization (“is-a”) | Build richer capability sets |
When should I use each?
Use implements (interfaces) when:
- You want flexible contracts decoupled from implementations (e.g.,
PaymentGateway,Cache,Repository). - You need multiple behaviors without tight coupling.
- You care about testability (mock/fake implementations in unit tests).
- You’re designing hexagonal/clean architecture ports and adapters.
Use extends (class inheritance) when:
- There’s a strong “is-a” relationship and shared state/behavior that truly belongs in a base class.
- You’re refining behavior of a framework base class (e.g.,
HttpServlet,Thread,java.iostreams). - You need protected hooks / template methods for controlled extension.
Avoid overusing inheritance when composition (a field that delegates) is clearer and safer.
Why do we need them? Importance & benefits
- Abstraction & decoupling: Interfaces let you program to capabilities, not concrete types, enabling swap-in implementations.
- Reuse & specialization: Inheritance centralizes common behavior, reducing duplication (when it’s a true fit).
- Polymorphism: Callers depend on supertype/interface; implementations can vary behind the scenes.
- API evolution: Interfaces with
defaultmethods allow additive changes with fewer breaking changes. - Testability: Interfaces create clean boundaries for mocks/stubs; inheritance can provide test doubles via small overrides.
Practical examples (real-world flavored)
Spring Boot service port with an adapter
public interface EmailSender {
void send(String to, String subject, String body);
}
@Service
public class SmtpEmailSender implements EmailSender {
// inject JavaMailSender, etc.
public void send(String to, String subject, String body) { /* ... */ }
}
// Usage: depend on EmailSender in controllers/use-cases, not on SMTP details.
Specializing a framework class (carefully)
public class AuditInputStream extends FilterInputStream {
public AuditInputStream(InputStream in) { super(in); }
@Override public int read() throws IOException {
int b = super.read();
// audit logic...
return b;
}
}
Modern features & gotchas
- Default methods conflict: If
AandBdefine the samedefault m(), a classimplements A, Bmust overridem()to resolve the diamond. - Abstract classes vs interfaces:
- Use abstract classes when you need shared state, partial implementations, or constructors.
- Use interfaces to define capabilities and support multiple inheritance of type.
- Sealed classes (Java 17+): Control which classes can
extenda base:public sealed class Token permits JwtToken, ApiKeyToken { } - Records: Can
implementsinterfaces, great for DTOs with behavior contracts:public record Money(BigDecimal amount, Currency currency) implements Comparable<Money> { ... }
Integration into your team’s software development process
1) Architecture & layering
- Define ports as interfaces in application/core modules (e.g.,
PaymentProcessor,UserRepository). - Implement adapters in infrastructure modules (
JdbcUserRepository,StripePaymentProcessor). - Expose services via interfaces; keep controllers/use-cases depending on interfaces only.
2) Coding standards
- Guideline: Prefer
implements+ composition; justify anyextendsin code review. - Naming: Interfaces describe capability (
*Service,*Repository,*Gateway); implementations are specific (Jdbc*,S3*,InMemory*). - Visibility: Keep base classes package-private when possible; avoid
protectedfields. - Final classes/methods: Mark classes
finalunless a deliberate extension point.
3) Testing
- Unit tests mock interfaces (Mockito/Stub implementations).
- For inheritance, favor template methods and override only documented hooks in tests.
4) Code review checklist
- Is this a true “is-a”? If not, prefer composition.
- Are we depending on interfaces at boundaries?
- Could an interface with a
defaulthelp evolve this API safely? - Are we avoiding deep inheritance chains (max depth 1–2)?
5) Tooling & enforcement
- Add static analysis rules (e.g., Error Prone/Checkstyle/Sonar) to flag deep inheritance and unused
protectedmembers. - Architectural tests (ArchUnit) to enforce “controllers depend on ports, not on adapters.”
Common pitfalls & how to avoid them
- “God” base classes: Too much logic in a superclass → fragile subclasses. Split responsibilities; use composition.
- Leaky abstractions: Interfaces that expose implementation details limit flexibility. Keep them capability-focused.
- Over-mocking concrete classes: Depend on interfaces at boundaries to keep tests simple and fast.
- Default method ambiguity: If combining interfaces with overlapping defaults, override explicitly.
FAQ
Can an interface extend a class?
No. Interfaces can only extend interfaces.
Can a class both extend and implement?
Yes: class C extends Base implements I1, I2 { ... }.
Is multiple inheritance supported?
For classes: no. For interfaces: yes (an interface may extend multiple interfaces; a class may implement multiple interfaces).
Interface vs abstract class—quick rule of thumb?
Need shared state/constructors → abstract class. Need flexible capability and multiple inheritance of type → interface.
Summary
- Reach for
implementsto define what something can do. - Use
extendsto refine how something does it—only when it’s truly the same kind of thing. - Bake these choices into your architecture, guidelines, tests, and tooling to keep designs flexible and maintainable.
Recent Comments