Adapter Pattern in Java (with Example)
The Adapter pattern explained: wrap a class with an incompatible interface so it works with the interface your code expects — like a travel plug adapter. Integrating third-party/legacy code, with Java code and UML.
🟢 Design Patterns · Structural · Fresher → 🟠senior
The Adapter pattern is the software version of a travel plug adapter: your laptop's plug doesn't fit the wall socket in another country, so you slot in an adapter that bridges the two. In code, when you have a class with one interface but your code expects a different interface, an Adapter sits between them and translates. It's how you make incompatible pieces work together without changing either.
The problem
Your app talks to payment providers through a PaymentProcessor interface with a pay(amount) method. You integrate a third-party library, but its class has a different method — makePayment(cents, currency). You can't change the library, and you don't want to rewrite your app around its odd interface. The shapes just don't fit.
The solution
🟠Write an adapter that implements your interface and internally calls the third-party class, translating as needed:
interface PaymentProcessor { void pay(double amount); } // what your app expects
class StripeLibrary { // the incompatible class
void makePayment(long cents, String currency) { /*…*/ }
}
class StripeAdapter implements PaymentProcessor { // the adapter
private final StripeLibrary stripe = new StripeLibrary();
public void pay(double amount) {
stripe.makePayment(Math.round(amount * 100), "USD"); // translate!
}
}
// client uses YOUR interface, unaware of Stripe's shape:
PaymentProcessor p = new StripeAdapter();
p.pay(19.99);
Your app keeps calling pay(); the adapter quietly converts dollars to cents and calls Stripe's method. Neither your code nor the library changed — the adapter absorbed the mismatch.
Where you meet it
🟠Adapter is everywhere in real systems: wrapping third-party libraries behind your own interface (so you can swap the library later without touching the rest of your code), integrating legacy code with a modern API, and bridging data formats. Java's Arrays.asList() adapts an array to a List; I/O InputStreamReader adapts a byte stream to a character stream. Recognising these as adapters is a nice senior touch.
A hidden benefit: decoupling
🟠Adapter doesn't just fix a mismatch — it isolates your codebase from an external dependency. Because all your code talks to your PaymentProcessor interface, swapping Stripe for PayPal later means writing one new adapter, not editing the whole app. That's the Dependency Inversion benefit — depend on your abstraction, adapt the outside world to it.
When to use it
Use Adapter when you need to make an existing class work with an incompatible interface — especially with third-party or legacy code you can't modify. If you control both sides, it's usually cleaner to just make the interfaces match; Adapter shines precisely when you can't change one side.
The interview-ready summary
"Adapter wraps a class with an incompatible interface and exposes the one my code expects, translating between them — like a travel plug adapter. I implement my own target interface in an adapter class that delegates to the foreign object, converting arguments as needed. It's how I integrate third-party libraries or legacy code without changing either side, and it decouples my app from the external dependency so I can swap it later by writing a new adapter. Examples include Arrays.asList and InputStreamReader." Clear, with the decoupling insight."
What to read next
- Decorator Pattern — adding behaviour by wrapping
- Facade Pattern — a simple front over a complex subsystem
- All Design Patterns (hub)
- ← The complete System Design guide (hub)