Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Program to Interfaces
LLD

Program to Interfaces

Learn how depending on abstractions instead of concrete implementations improves flexibility and testability.

The Problem It Solves

ArrayList<String> names = new ArrayList<>();   // caller knows the concrete
names.sort(...);
// three months later: needs dedup on insert → HashSet? LinkedHashSet?
// every declared type, every parameter, every field must be retyped.

Declaring variables and parameters as concrete classes welds callers to today’s implementation choice. The coupling is invisible until the day the implementation must change — then it becomes a sweeping refactor. The principle: depend on contracts (List, Map, domain interfaces); let construction sites — ideally exactly one — know the concrete.

The Mechanism

      Caller code                Interface (contract)           Implementations
 ┌──────────────────┐       ┌───────────────────────┐       ┌──────────────────┐
 │ List<String> s;  │ ────► │ List / Sorter / Store │ ◄──── │ ArrayList        │
 │ uses add/get/... │       │  (stable surface)     │       │ LinkedList       │
 └──────────────────┘       └───────────────────────┘       │ CopyOnWriteList  │
                                                            └──────────────────┘
 Caller sees only the middle box. Right side can grow or swap freely.

The single rule in code:

List<String> names = new ArrayList<>();   // declare LEFT type as interface
void register(Sorter sorter)              // parameters: interfaces too

new ArrayList<>() still exists — but confined to one line. Swapping implementations becomes a one-line change plus zero caller edits.

What It Buys

  • Swappability: storage, algorithms, vendors replaceable behind stable seams.
  • Testability: any collaborater behind an interface can be stubbed — no framework required for a test seam.
  • Parallel work: teams code against agreed interfaces before implementations exist.
  • JDK ubiquity proves the point: nearly every JDK API returns List/Map/Set, never ArrayList/HashMap — Sun could change internal implementations across decades without breaking user code.

What It Costs

  • Indirection when reading: “what does get() actually do?” requires finding the implementation.
  • An interface with one implementation and no planned variant and no test purpose is pure ceremony.
  • Performance nuance: megamorphic call sites lose JIT inlining (see vtable page) — irrelevant for most code, real for hot loops.

Where NOT to Apply

SituationVerdict
Domain concept with known variants (payment modes, storages)Interface
Collaborator needing test stubsInterface
Internal helper class, private to one moduleSkip — direct class
Value types with no variation (Money)Skip
DTOs crossing boundariesSkip — data, not behavior

Interview Signals

  • Field and parameter declarations using interfaces on the whiteboard is visible instantly and scored.
  • The senior qualifier: naming why this beats always-interface (“seams cost reading effort; buy them only where change or testing is expected”).

My Private Notes

Notes are auto-saved locally to this device.