Adapter Pattern
Make incompatible interfaces work together by translating the interface expected by the client into calls understood by an existing component.

Concept Overview
The Adapter pattern converts the interface of an existing class into an interface the client expects. It acts as a bridge between incompatible interfaces without requiring the client or the existing library to be rewritten. It is commonly useful when integrating third-party libraries, legacy components or multiple providers that expose different method names and parameter conventions.
1. The problem: the client and an existing library disagree
Imagine a media player client expects play(type, fileName), but an existing advanced library exposes loadFile(fileName) and listen(). The client cannot call the library through its expected contract. Modifying the library may be impossible because it is third-party code, shared legacy code or outside the team's ownership. Duplicating special-case handling throughout the client spreads integration knowledge across the application.
2. The adapter translates the target contract
The Target is the interface the client understands. The Adaptee is the existing component with an incompatible interface. The Adapter implements the Target and delegates to the Adaptee, translating method calls and parameters as needed. The Client depends on the Target rather than the concrete Adaptee. In the supplied media-player example, AdvancedMediaPlayerAdapter maps MP4 or VLC playback requests to the corresponding library methods.
interface MediaPlayer {
void play(String type, String fileName);
}
interface AdvancedMediaPlayer {
void loadFile(String fileName);
void listen();
}
final class AdvancedMediaPlayerAdapter implements MediaPlayer {
private final AdvancedMediaPlayer player;
AdvancedMediaPlayerAdapter(AdvancedMediaPlayer player) {
this.player = player;
}
public void play(String type, String fileName) {
if ("mp4".equalsIgnoreCase(type)) {
player.loadFile(fileName);
player.listen();
}
}
}3. Participants and responsibilities
The Target defines the contract expected by the client. The Adaptee contains the existing behaviour. The Adapter implements the Target and holds or receives an Adaptee reference. The Client uses the Target contract and does not need to know which provider-specific implementation sits behind the adapter. This structure keeps interface translation at the integration boundary.
4. Object adapter and class adapter
An object adapter uses composition: it stores a reference to the adaptee and delegates calls. This is flexible and works even when the adaptee cannot be subclassed. A class adapter uses inheritance to adapt a concrete class, where the language and inheritance model allow it. The supplied infographic demonstrates the object-adapter approach. Composition is usually the more flexible choice in Java application code.
5. When to use Adapter
Use Adapter when an existing class or external API provides the right capability through the wrong interface, when integrating unrelated or legacy systems, or when several providers need to satisfy one application contract. Avoid an adapter that merely renames a method without providing useful decoupling, or one that silently discards important provider errors. Translation should preserve relevant semantics, exceptions and lifecycle requirements.
Key Takeaways
- ✓Adapter translates an existing interface into the contract expected by the client.
- ✓The client depends on the Target abstraction rather than the Adaptee.
- ✓Composition-based object adapters are flexible and avoid tight inheritance coupling.
- ✓Adapters belong at integration boundaries and should preserve meaningful behaviour and errors.