Overview
An interface declares what operations a type supports without implementing them; any class can implement many interfaces. An abstract class can contain both implemented and abstract methods plus state, but a class can extend only one (in Java, C#, TypeScript).
Use interfaces to define capabilities and contracts between components (Notifier, Repository), which keeps coupling low and allows multiple implementations and test fakes. Use abstract classes to share code and state among closely related subclasses, often as a Template Method skeleton. In interviews, starting from interfaces is almost always the stronger design.
An interface is a job description: anyone who can do these tasks qualifies. An abstract class is an apprentice who already knows some skills and needs to learn the rest; you can only have one master to learn from.
When to use it
- Interface: a capability many unrelated classes might have (Comparable, Payable).
- Interface: boundaries between modules, for dependency inversion and testing.
- Abstract class: shared state and algorithm skeleton for a family of classes.
Where it shows up in interviews
Recognize it when: define boundaries before implementations.
- Design a payment gateway
- Design a storage abstraction
Recognize it when: subclasses repeat the same steps with small differences.
- Design report exporters
- Design a data import pipeline
Where it is used in real software
List is an interface; AbstractList is an abstract class that implements most of it so ArrayList and others only fill in a few methods.
Go uses implicit interfaces like io.Reader, satisfied by any type with the right methods.
Any object with matching members satisfies an interface, which makes fakes in tests trivial.
Key terms
- Interface
- Contract of method signatures; multiple can be implemented.
- Abstract class
- Cannot be instantiated; may have state and implemented methods.
- Default method
- Java interface method with a body, for evolving interfaces.
- Abstract method
- Declared without a body; subclasses must implement.
How it works, step by step
- 1Start with the interface
What does the caller need?
- 2Keep it small and cohesive
Split fat interfaces (ISP).
- 3Add an abstract base only for real shared code
Common state or algorithm skeleton.
- 4Implement concrete classes
Extend the base or implement the interface directly.
- 5Depend on the interface in callers
Never on the abstract base unless required.
Interface vs abstract class
Key differences (Java / TypeScript)
| Aspect | Interface | Abstract class |
|---|---|---|
| Multiple inheritance | Implement many | Extend one |
| State (fields) | Constants only (Java) | Yes |
| Constructors | No | Yes |
| Implemented methods | Default methods (Java 8+) | Yes |
| Best for | Capabilities and boundaries | Shared code in a family |
NOWAspect: Multiple inheritance | Interface: Implement many | Abstract class: Extend one
Common combination: an interface as the public contract, plus an optional abstract base implementing shared parts.
Implementation
public interface Exporter { byte[] export(List<Row> rows);} // Abstract base: shared skeleton for text-based exporterspublic abstract class TextExporter implements Exporter { @Override public final byte[] export(List<Row> rows) { StringBuilder out = new StringBuilder(header()); for (Row row : rows) out.append(formatRow(row)).append('\n'); return out.toString().getBytes(StandardCharsets.UTF_8); } protected abstract String header(); protected abstract String formatRow(Row row);} public final class CsvExporter extends TextExporter { protected String header() { return "id,name,total\n"; } protected String formatRow(Row r) { return r.id() + "," + r.name() + "," + r.total(); }} // A binary exporter implements the interface directly, no base class neededpublic final class ParquetExporter implements Exporter { public byte[] export(List<Row> rows) { return ParquetWriter.write(rows); }}Complexity and performance
Mix capabilities.
Single inheritance.
Trade-offs
Interfaces maximize flexibility; abstract classes maximize code reuse but couple subclasses to the base.
Adding a method to an interface breaks implementers unless a default is provided.
Variants and related techniques
Single-method interfaces usable with lambdas (Comparator, Runnable).
No methods, only a type tag (Serializable); annotations are often better.
Common mistakes
- Depending on the abstract base in callers.
Fix: Depend on the interface so other implementations remain possible.
- Fat interfaces.
Fix: Split into role interfaces.
Interview questions
When would you choose an abstract class over an interface?
When closely related subclasses share state or a fixed algorithm skeleton that should be implemented once. For contracts between components or capabilities across unrelated classes, use interfaces.
Can Java interfaces have implementations?
Yes, since Java 8 they can have default and static methods, and since Java 9 private methods, but they still cannot hold instance state.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Design Exporter for CSV, JSON, PDF | Easy | Interface + base. |
| Design a Shape hierarchy with Drawable and Resizable | Easy | Multiple interfaces. |