Adapter Pattern in Java: Simple Guide with Real Example
Adapter Pattern is the first structural pattern in our series. It solves a very common problem: you have two things that should work together, but they do not fit. In this guide, I will explain it in plain words with a real travel plug adapter example that you have probably used yourself.
What is Adapter Pattern, in simple words?
Adapter Pattern is a way to make two incompatible things work together — without changing either one.
Here is the idea. You have a class that does exactly what you need, but its method names or parameter shapes do not match what your code expects. You cannot change the class (it might be third-party code). You do not want to change your code (it works fine). So what do you do?
You write a small class in between — an adapter. It accepts the calls your code makes, translates them into calls the existing class understands, and returns the results in the shape your code expects.
Think of it this way. Your code speaks English. The existing class speaks French. They both want to talk. The adapter is a translator standing in the middle.
Neither side knows the other exists. The adapter hides everything.
That is the whole idea: translate between two interfaces, hide the translation from both sides.
A real-life example: travel plug adapter
You have probably used one of these.
Imagine you are traveling from India to the USA. You pack your laptop charger. It has an Indian-style plug — two round pins with a specific shape. You reach the US hotel room. The wall socket is completely different. Flat pins, different spacing.
Your charger will not fit. It is not broken. The socket is not broken. They just do not match.
What do you do? You buy a plug adapter at the airport. One end fits the US socket. The other end accepts your Indian plug. In the middle, it silently translates the electrical connections.
Now your charger works. The socket does not know it was not designed for the charger. The charger does not know it is not plugged into an Indian socket. The adapter hides everything.
That is Adapter Pattern in one picture. Make the incompatible compatible, without changing either side.
Now think about code. You have a library that does exactly what you need, but its method is called printText() instead of display(). You cannot change the library. You do not want to change your code everywhere. You write a small adapter. Problem solved.
Building it in Java step by step
Let us build a realistic example: an old payment system and a new one. Your modern e-commerce code expects a PaymentProcessor interface, but you have an old legacy class called LegacyPaymentGateway with a different method name.
Step 1: Define what your code expects
Your application is written against this interface:
public interface PaymentProcessor {
void processPayment(double amount);
}
Every payment class in your modern code follows this. That is your target interface.
Step 2: The existing class you cannot change
The old payment system has a class like this:
public class LegacyPaymentGateway {
public void makePayment(String amountInString) {
System.out.println("Legacy gateway: Processing $" + amountInString);
}
}
Notice the problem. The method is makePayment(), not processPayment(). And it takes a String, not a double.
You cannot change this class — it is third-party code that you do not own.
Step 3: Create the adapter
Now write a small class that bridges the two:
public class LegacyPaymentAdapter implements PaymentProcessor {
private final LegacyPaymentGateway legacyGateway;
public LegacyPaymentAdapter(LegacyPaymentGateway legacyGateway) {
this.legacyGateway = legacyGateway;
}
@Override
public void processPayment(double amount) {
// Translate: method name and parameter type
legacyGateway.makePayment(String.valueOf(amount));
}
}
That is the whole adapter. It implements the interface your code expects. Inside, it wraps the legacy class and translates calls.
Your code does not know there is a legacy class under the hood. It just calls processPayment().
Step 4: Use it in your code
public class Checkout {
private PaymentProcessor paymentProcessor;
public Checkout(PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
public void pay(double amount) {
paymentProcessor.processPayment(amount);
}
public static void main(String[] args) {
// Old legacy system wrapped in the adapter
LegacyPaymentGateway legacy = new LegacyPaymentGateway();
PaymentProcessor adapter = new LegacyPaymentAdapter(legacy);
Checkout checkout = new Checkout(adapter);
checkout.pay(99.99);
}
}
Output
Legacy gateway: Processing $99.99
Look at what happened. Your Checkout class has no idea it is talking to a legacy system. It called processPayment() just like it would with any modern payment method. The adapter did the translation silently.
That is Adapter Pattern in about 20 lines of code.
What if the legacy class is replaced?
Suppose next year the company replaces LegacyPaymentGateway with a new system. Your Checkout class does not change. You just write a new adapter, or use a modern PaymentProcessor directly. Everything keeps working.
That is the power of the pattern. It isolates the incompatible part into a small, replaceable class.
Two types of Adapter: Class vs Object
There are two ways to write an Adapter in Java. They both work, but they have different trade-offs.
Type 1: Object Adapter (what we just wrote)
Uses composition. The adapter holds a reference to the object being adapted.
public class LegacyPaymentAdapter implements PaymentProcessor {
private final LegacyPaymentGateway legacyGateway; // holds the object
public LegacyPaymentAdapter(LegacyPaymentGateway legacyGateway) {
this.legacyGateway = legacyGateway;
}
public void processPayment(double amount) {
legacyGateway.makePayment(String.valueOf(amount));
}
}
Why it is preferred: It works with any subclass of LegacyPaymentGateway. You can also adapt multiple classes. It is more flexible.
Type 2: Class Adapter (uses inheritance)
Uses inheritance. The adapter extends the legacy class and implements the target interface.
public class LegacyPaymentAdapter extends LegacyPaymentGateway implements PaymentProcessor {
@Override
public void processPayment(double amount) {
makePayment(String.valueOf(amount));
}
}
Why it is less preferred: It only works if the legacy class is not final and has an accessible constructor. It cannot adapt two classes at once (Java does not support multiple inheritance).
Which one should you use?
Almost always Object Adapter. It is more flexible, cleaner, and easier to test. Use Class Adapter only when you specifically need to inherit behavior from the legacy class.
Adapter vs Decorator vs Facade
These three patterns are often confused because they all "wrap" other objects. But they solve different problems.
| Pattern | Purpose | Changes Interface? | Example |
|---|---|---|---|
| Adapter | Make incompatible things work together | Yes — translates to a new interface | Travel plug adapter |
| Decorator | Add new behavior without changing the class | No — same interface, more features | BufferedReader wrapping FileReader |
| Facade | Simplify a complex system behind one interface | Yes — but hides complexity, not translates | SLF4J hiding Log4j |
The one-line difference
- Adapter: "You speak French. I speak English. Let me translate."
- Decorator: "You already speak English. Let me add some new words."
- Facade: "You have 10 people talking. Let me give you one person to talk to."
Use Adapter when the interface is wrong. Use Decorator when you need to add features. Use Facade when the system is too complex.
When to use it (and when not to)
Good times to use Adapter Pattern
- You want to use a third-party library, but its interface does not match your code
- You are integrating with a legacy system
- You want to reuse an old class without modifying it
- You are building a plugin system where different plugins need to fit the same interface
- You want to isolate your code from a library so you can swap it out later
Bad times to use Adapter Pattern
- You can just change the interface of one side — sometimes that is simpler
- The mismatch is trivial — just call the method directly
- You are adding the adapter "just in case" — that is over-engineering
- You want to add features — that is Decorator's job
The rule I use: if you cannot change either side and they genuinely do not fit, use an Adapter. Otherwise, just fix the code.
Where you will see it in real Java code
Adapter Pattern is everywhere in Java. You have probably used it today without realizing.
- InputStreamReader — takes an
InputStream(byte stream) and adapts it to aReader(character stream). Classic Adapter. - Arrays.asList() — takes an array and adapts it to a
List. Lets you use array methods likeadd()andget(). - Java's Collections.unmodifiableList() — adapts a mutable list to a read-only view.
- Spring's HandlerAdapter — adapts different types of request handlers to a common internal interface.
- JDBC drivers — every database has its own protocol. The JDBC driver adapts the database's native API to the standard Java
Connection/Statementinterface. - Log4j 1.x → SLF4J bridge — a class that lets old Log4j code use the modern SLF4J API.
- Android's RecyclerView.Adapter — adapts your data (custom list) into views the RecyclerView understands.
See the pattern? Every one of these bridges two things that would not otherwise work together. That is Adapter Pattern at work.
Common mistakes to avoid
1. Adapter doing too much
An Adapter should only translate. If it starts adding logic, caching, validating, or doing calculations, it has stopped being an Adapter.
Fix: Keep the adapter as thin as possible. If you need extra behavior, that is a Decorator.
2. Confusing it with Facade
Facade is often confused with Adapter. Remember the difference: Adapter translates between two interfaces. Facade simplifies many interfaces behind one.
Fix: If you are wrapping one thing, it is Adapter. If you are wrapping many things, it is Facade.
3. Using Class Adapter when Object Adapter works
Class Adapter uses inheritance. It has limitations — cannot adapt multiple classes, breaks if the class is final. Object Adapter is almost always better.
Fix: Use Object Adapter by default. Only use Class Adapter when you have a specific reason.
4. Adapter that leaks the wrapped class
If your code can still see the legacy class through the adapter, the abstraction is broken. The adapter should fully hide the underlying implementation.
Fix: Keep the wrapped object private. Do not return it from any method.
5. Adapter that changes semantics
If you translate pay() into makePayment(), that is fine. But if you translate pay() into cancel(), the semantics are wrong. The adapter should not change what the method does, only how it is called.
Fix: Adapters translate shape, not meaning. If the meanings differ, you need a different design.
Common questions
What is Adapter Pattern in simple words?
Adapter Pattern is a design pattern where you create a wrapper class that lets two incompatible interfaces work together. It is like a travel plug adapter that lets an Indian plug fit into a US socket.
Why do we need Adapter Pattern in Java?
We need it when we want to use an existing class but its interface does not match what our code expects. Instead of rewriting the class or changing our code, we write a small adapter class that translates between the two.
What is the difference between Adapter and Decorator Pattern?
Adapter changes the interface of an object so it can be used in a new way. Decorator adds new behavior to an object without changing its interface. Adapter is about compatibility, Decorator is about features.
What are the two types of Adapter Pattern?
There are two types: Class Adapter (uses inheritance) and Object Adapter (uses composition). Object Adapter is preferred because it is more flexible and works with any subclass.
Where is Adapter Pattern used in real Java code?
Adapter Pattern is used in Java's InputStreamReader (adapts InputStream to Reader), Arrays.asList() (adapts array to List), Spring's HandlerAdapter, JDBC drivers, and many third-party library integrations. Every time you use an old library with new code, you are likely using Adapter Pattern.
Is Adapter Pattern the same as Wrapper Pattern?
Yes, mostly. Adapter Pattern is sometimes called Wrapper Pattern because it wraps one object to make it look like another. But note that Decorator Pattern is also sometimes called Wrapper. The term "adapter" is clearer.
Where to go next
You now understand Adapter Pattern better than most working developers. You know the two types, when to use each, and the mistakes to avoid.
If you remember one thing, remember this: Adapter Pattern is about making incompatible things work together. It is a translator, not a feature-adder, not a simplifier. Just a translator.
You have used Adapter Pattern without realizing it many times. InputStreamReader, Arrays.asList(), JDBC drivers — they are all adapters. Now you know what is going on under the hood.
If this helped, bookmark it. But the real learning happens when you type the code. Take a third-party library you use — even a simple one — and wrap it in an adapter. That exercise will teach you more than any tutorial.