Interfaces
An interface is a contract: the operations a type promises, with no say in how they are done. Code that depends on the contract can work with any implementation.
Overview
An interface lists operations and their signatures, and nothing else. A class that implements the interface promises to provide every one of them. Code that needs those operations depends on the interface, not on any particular class, so any implementation can be handed in: a real payment gateway in production, a fake in tests, a new provider next year.
This is the most useful tool in low-level design. Almost every pattern (Strategy, Observer, Repository, Adapter) is an interface plus a rule about who implements it. Interviewers listen for whether you introduce an interface at the right seams: where behavior varies, where a dependency is slow or external, and where two teams need to work in parallel.
A contract with no body
In Java the rules are strict and worth knowing exactly. A method declared in an interface without a body is implicitly public abstract. A field declared in an interface is implicitly public static final, so it is a constant, not state. A class may implement any number of interfaces, and an interface may extend several others. Since Java 8 an interface may also carry default methods (with a body, inherited by implementers) and static methods; since Java 9, private helper methods for those bodies.
- Abstract methods — yes, implicitly public — the contract itself
- Constants — yes, implicitly public static final — shared names, never state
- Instance fields — no — an interface has no state to protect
- Default methods — yes, since Java 8 — evolve a published contract without breaking implementers
- Static methods — yes, since Java 8 — factories and helpers tied to the contract
- Constructors — no — you never instantiate a contract
Program to the interface, not the implementation
The rule of thumb is old and still the best one: a class should hold, accept and return interfaces, and name concrete classes only at the place where objects are created. The gain shows up in four places.
- Testing Checkout — needs a real card gateway, network, secrets — pass a FakePayment that records calls
- Adding wallet payments — edit Checkout — add WalletPayment; Checkout is untouched
- Two teams, one deadline — the caller waits for the implementation — agree the interface first; both build at once
- Reading the code — you must read the gateway to know what Checkout needs — the interface says exactly what it needs
The Dependency Inversion Principle, from Robert C. Martin's SOLID set, is this rule stated for whole modules: depend in the direction of abstraction, so that high-level policy does not depend on low-level detail.
How languages spell a contract
Languages differ in whether a class must say that it implements an interface. The design idea is the same; the checking happens at different times.
- Java, C# — interface, class X implements I — compile time — yes, explicitly
- Go — type I interface { ... } — compile time — no: any type whose method set covers the interface implements it
- Python — abc.ABC with @abstractmethod — at instantiation: a class with an unimplemented abstract method cannot be created — yes, by subclassing (or register)
- Python — typing.Protocol — by a static type checker; @runtime_checkable allows isinstance, which checks only that the methods exist — no: structural, like Go
- Python — duck typing — at the call: the method is simply called — no
- C++ — a class whose methods are all pure virtual — compile time — yes, by inheriting
Interface or abstract class
Both let you write code against an abstraction. The difference is whether the abstraction carries shared state and shared code.
- State — none (constants only) — fields, any visibility
- Shared code — default methods only, no fields to use — concrete methods over real fields
- How many — a class implements many — a class extends one
- Relationship it models — 'can do': Comparable, Closeable, PaymentMethod — 'is a kind of': AbstractList, Shape
- Best for — unrelated classes that share a capability — closely related classes that share code and fields
What you will be able to do
- Introduce an interface at a seam where behavior varies or a dependency is external, and defend the choice.
- State the Java rules (implicit modifiers, multiple implementation, default methods) without hesitating.
- Choose between an interface and an abstract class with a reason the interviewer will accept.
- Explain nominal versus structural contracts across Java, Go and Python.
- Split a fat interface so that callers depend only on what they use.
Interview questions
- When would you choose an interface over an abstract class?
- Why should code depend on an interface rather than a concrete class?
- How do interfaces give you multiple inheritance without the diamond problem?
- What is duck typing, and when do you want an explicit interface instead?
- How do you keep an interface from growing into a fat one?
Sign up free to read the full topic
More Low-Level Design topics
Start free on Cariara · Coding problems · System design studio