Chaturmind
LearnDSASystem DesignInterview PrepDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth
  • Java Interview Prep

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Interview Prep: 5–8 Years

Advanced Core Java

  • Advanced OOP & Design Scenarios — Interview Questions
  • Advanced Concurrency, Collections & Memory — Interview Questions
  • Modern Java Language Features & Annotations — Interview Questions
  • Class Loading, Reflection, Serialization & Idioms — Interview Questions

Java Design Patterns in Depth

  • Design Patterns Overview & Singleton — Interview Questions
  • Factory & Abstract Factory Patterns — Interview Questions
  • Builder & Prototype Patterns — Interview Questions
  • Adapter & Bridge Patterns — Interview Questions
  • Composite & Decorator Patterns — Interview Questions
  • Facade & Proxy Patterns — Interview Questions
  • Chain of Responsibility & Observer Patterns — Interview Questions
  • Strategy & Template Method Patterns — Interview Questions
  • Command Pattern — Interview Questions

Advanced Spring Boot

  • Logging, Configuration & Actuator (Advanced) — Interview Questions
  • Transactions, Multiple Datasources & Query Tuning — Interview Questions
  • Validation & REST API Design (Advanced) — Interview Questions
  • Reactive, Async & Scheduling in Spring Boot — Interview Questions
  • Deployment, High Availability, Scaling & Caching — Interview Questions
Chaturmind
← Java Interview Prep: 5–8 Years

Advanced Core Java

  • Advanced OOP & Design Scenarios — Interview Questions
  • Advanced Concurrency, Collections & Memory — Interview Questions
  • Modern Java Language Features & Annotations — Interview Questions
  • Class Loading, Reflection, Serialization & Idioms — Interview Questions

Java Design Patterns in Depth

  • Design Patterns Overview & Singleton — Interview Questions
  • Factory & Abstract Factory Patterns — Interview Questions
  • Builder & Prototype Patterns — Interview Questions
  • Adapter & Bridge Patterns — Interview Questions
  • Composite & Decorator Patterns — Interview Questions
  • Facade & Proxy Patterns — Interview Questions
  • Chain of Responsibility & Observer Patterns — Interview Questions
  • Strategy & Template Method Patterns — Interview Questions
  • Command Pattern — Interview Questions

Advanced Spring Boot

  • Logging, Configuration & Actuator (Advanced) — Interview Questions
  • Transactions, Multiple Datasources & Query Tuning — Interview Questions
  • Validation & REST API Design (Advanced) — Interview Questions
  • Reactive, Async & Scheduling in Spring Boot — Interview Questions
  • Deployment, High Availability, Scaling & Caching — Interview Questions
HomeLearnJava Interview PrepJava Interview Prep: 5–8 YearsJava Design Patterns in Depth
✓ FreeAdvanced· 7 min read

Adapter & Bridge Patterns — Interview Questions

Adapter's purpose and use cases, Adapter vs Decorator, a Java example (legacy logging, third-party SDKs), class vs object adapters in Java, why adapters are an anti-corruption layer for third-party libraries, and Bridge — decoupling abstraction from implementation, Bridge vs Adapter, implementation, scenarios and benefits.

Published September 25, 2026


How to use this lesson

Adapter and Bridge have similar structures (a wrapper holding a reference) but opposite intents. Adapter fixes a mismatch after the fact. Bridge is designed up front, to let two dimensions vary independently. Making that distinction crisply is what interviewers look for.

Q1. What is the Adapter pattern for?

Short answer: An adapter converts the interface of an existing class into the interface a client expects, so incompatible components can work together without changing either one. Typical uses:

  • integrating third-party SDKs;
  • wrapping legacy code;
  • migrating between APIs;
  • normalising several providers behind one interface.

Example: your checkout depends on a PaymentGateway interface. A new provider's SDK has a different API, so an adapter implements PaymentGateway by calling the SDK.

Learn it in depth → Adapter & Facade

Q2. What is the Adapter pattern? (The section introduction: its structure)

Short answer: It has four participants:

  • the Target: the interface the client uses;
  • the Adaptee: the existing class, with an incompatible API;
  • the Adapter: implements Target, and translates calls to the Adaptee;
  • the Client: knows only Target.

The translation may cover method names, parameter types, units and currencies, error models (mapping SDK exceptions to your own), and synchronous vs asynchronous styles.

Q3. What is the Adapter pattern, and when would you use it?

Short answer: Use it when you can't or shouldn't change either side: third-party or legacy code, or generated clients. And you want your domain code to depend on your own abstraction. It's the core building block of an anti-corruption layer in domain-driven design, which stops foreign models from leaking into your core.

public interface PaymentGateway { ChargeResult charge(Money amount, String customerToken); }

public final class AcmePayAdapter implements PaymentGateway {        // object adapter
    private final AcmePayClient sdk;                                  // adaptee (third-party)
    public AcmePayAdapter(AcmePayClient sdk) { this.sdk = sdk; }

    @Override public ChargeResult charge(Money amount, String token) {
        try {
            AcmeResponse r = sdk.createCharge(amount.minorUnits(), amount.currency().getCurrencyCode(), token);
            return r.isSuccess() ? ChargeResult.approved(r.getId()) : ChargeResult.declined(r.getDeclineCode());
        } catch (AcmeTimeoutException e) {
            throw new PaymentUnavailableException("AcmePay timeout", e);   // translate the error model too
        }
    }
}

Q4. How does Adapter differ from Decorator?

Short answer: An Adapter changes the interface, making X look like Y, while keeping the behaviour. A Decorator keeps the same interface, and adds behaviour (logging, caching, retries, metrics). Decorators can be stacked transparently. Adapters sit at the boundary between two different interfaces.

AdapterDecorator
InterfaceConverts to a different oneThe same as the wrapped object
GoalCompatibilityExtra responsibilities
StackingRarelyCommonly (new Buffered(new Gzip(new File…))))

Q5. Give a Java example of the Adapter pattern.

Short answer: SLF4J bridges are adapters: jul-to-slf4j and log4j-over-slf4j let code written for one logging API emit through SLF4J and Logback. In the JDK, Arrays.asList adapts an array to the List interface, and InputStreamReader adapts a byte stream (InputStream) to a character stream (Reader).

// Adapting a legacy logger to your Logger abstraction
public final class LegacyLoggerAdapter implements AppLogger {
    private final OldLogUtil legacy;
    public LegacyLoggerAdapter(OldLogUtil legacy) { this.legacy = legacy; }
    @Override public void info(String msg, Object... args) { legacy.write(1, MessageFormat.format(msg, args)); }
    @Override public void error(String msg, Throwable t) { legacy.write(3, msg + " — " + t); }
}

Q6. What are class adapters and object adapters, and how do they differ?

Short answer:

  • An object adapter (composition) holds a reference to the adaptee, and implements the target interface. It works with the adaptee and its subclasses, can adapt several adaptees, and is the usual choice in Java.
  • A class adapter (inheritance) extends the adaptee class and implements the target interface, overriding or forwarding methods. Java has single class inheritance, so the target must be an interface, and it adapts only that one concrete class.
class AcmePayClassAdapter extends AcmePayClient implements PaymentGateway {   // class adapter
    public ChargeResult charge(Money amount, String token) { /* call inherited createCharge(...) */ return null; }
}

Common trap: "a class adapter extends both the target and the adaptee". That's impossible in Java when both are classes.

Q7. Why is Adapter useful when integrating third-party libraries?

Short answer:

  • Isolation: vendor types and exceptions stay inside the adapter, so the domain depends on your interfaces.
  • Replaceability: switching providers means writing a new adapter.
  • Testability: mock or fake your interface, not the SDK.
  • Upgrade safety: SDK breaking changes are contained in one class.
  • A consistent error and retry policy.
  • Multi-provider routing: several adapters behind one interface, chosen by a strategy or factory.

Q8. What is the Bridge pattern, and how does it decouple abstraction from implementation?

Short answer: Bridge splits a design into two independent hierarchies:

  • an abstraction: the high-level API, such as Notification → UrgentNotification, DigestNotification;
  • an implementor: the low-level operations, such as MessageSender → EmailSender, SmsSender, PushSender.

The abstraction holds a reference to an implementor, the bridge. The two can then be combined freely, and extended independently: N abstractions × M implementations with N + M classes, not N × M subclasses.

Learn it in depth → Bridge & Flyweight

Q9. What's the difference between Bridge and Adapter?

Short answer: Intent and timing. Bridge is designed up front, so that two dimensions can vary independently: it prevents a class explosion. Adapter is applied after the fact, to make existing, incompatible interfaces work together. Structurally, both delegate to a wrapped object. Bridge's two sides are designed together, while Adapter bolts together interfaces that weren't designed for each other.

Q10. How would you implement the Bridge pattern in Java?

Short answer:

public interface MessageSender { void send(String to, String body); }            // the implementor
final class EmailSender implements MessageSender { public void send(String to, String b) { /* SMTP */ } }
final class SmsSender implements MessageSender { public void send(String to, String b) { /* SMS API */ } }

public abstract class Notification {                                               // the abstraction
    protected final MessageSender sender;                                          // ← the bridge
    protected Notification(MessageSender sender) { this.sender = sender; }
    public abstract void notify(User user, String event);
}
final class UrgentNotification extends Notification {                             // refined abstraction
    UrgentNotification(MessageSender s) { super(s); }
    public void notify(User u, String event) { sender.send(u.contact(), "URGENT: " + event); }
}
final class DigestNotification extends Notification {
    private final List<String> buffer = new ArrayList<>();
    DigestNotification(MessageSender s) { super(s); }
    public void notify(User u, String event) { buffer.add(event); if (buffer.size() == 10) flush(u); }
    private void flush(User u) { sender.send(u.contact(), String.join("\n", buffer)); buffer.clear(); }
}
// any abstraction × any sender: new UrgentNotification(new SmsSender()), new DigestNotification(new EmailSender())

Q11. In what scenarios would you use Bridge?

Short answer: Whenever two dimensions of variation would otherwise multiply subclasses:

  • shapes × rendering APIs;
  • notifications × channels;
  • reports × output formats (PDF, CSV, Excel);
  • persistence abstractions × storage engines;
  • a device driver abstraction × platforms.

Also: when you want to switch implementations at runtime, or keep platform-specific code out of the business hierarchy.

Key points to cover:

  • JDBC is a classic example. Your code uses the java.sql abstractions, and each vendor's driver provides the implementation, which can vary independently.

Q12. What are the key benefits of Bridge in large systems?

Short answer:

  • No combinatorial class explosion.
  • Independent evolution: platform or vendor teams extend implementations while product teams extend abstractions.
  • Runtime flexibility: swap implementations through configuration.
  • Better testability: fake implementors.
  • Clearer separation of business intent from technical detail.

The costs: more indirection, and an up-front design effort. Use it where both dimensions really vary.

Follow-up questions this topic invites — and their answers

Q: What is a two-way adapter? A: An adapter that implements both interfaces, so each side can call the other through it. It's useful during migrations, but it's more complex. Use it sparingly.

Q: How does an anti-corruption layer differ from a single adapter? A: An ACL is a whole translation boundary (often several adapters, translators and facades) that protects a bounded context's model from an external system's model. The Adapter pattern is one of its building blocks.

Q: Is Spring's HandlerAdapter an Adapter? A: Yes. The DispatcherServlet calls every kind of handler (annotated controllers, HttpRequestHandlers, functional endpoints) through one HandlerAdapter interface, with a different adapter for each handler type.

Q: Bridge vs Strategy? A: Both delegate to an interchangeable object. Strategy varies one algorithm used by a context. Bridge separates a whole abstraction hierarchy from an implementation hierarchy, both of which are extended.

Previous

Builder & Prototype Patterns — Interview Questions

Next

Composite & Decorator Patterns — Interview Questions

AI Tutor

Lesson: Adapter & Bridge Patterns — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.