What is the difference between an object adapter and a class adapter in Java, and which does Java favor?
answer
- Object adapter = has-a (field + delegate)
- Class adapter = is-a (extends adaptee, implements target)
- Single inheritance burns the one extends slot
- Composition wraps any subtype / swappable at runtime
- JDK adapters favor composition
basics
~20 sAn object adapter holds the adaptee as a field and delegates to it (composition). A class adapter extends the adaptee and implements the target (inheritance). Java favors the object adapter because Java allows only single class inheritance.
solid answer
~50 sBoth forms implement the Target interface, but they differ in how they reach the Adaptee. An object adapter uses composition: it stores the adaptee in a field and forwards calls to it. A class adapter uses inheritance: it extends the adaptee class and implements the target interface, so it IS the adaptee. Java strongly favors the object adapter because a class can extend only one class, so the class-adapter form burns your single inheritance slot and can't adapt an adaptee given by interface or chosen at runtime. Composition is more flexible: the adapter can wrap any subtype of the adaptee, swap the instance, and adapt several adaptees. The class adapter's only edge is that it can override adaptee methods directly and needs no forwarding boilerplate. Most JDK adapters lean on composition for exactly these reasons.
code
java · 11 lines// Object adapter (composition) - Java's preferred form
class ObjectAdapter implements Target {
private final Adaptee adaptee; // has-a
ObjectAdapter(Adaptee a) { this.adaptee = a; }
public String fetch() { return adaptee.specificRequest(); }
}
// Class adapter (inheritance) - limited by single inheritance
class ClassAdapter extends Adaptee implements Target {
public String fetch() { return specificRequest(); } // is-a
}go deeper
Knows the two forms exist: one wraps the adaptee in a field, one extends it.
Clearly contrasts composition vs inheritance forms and states Java prefers the object adapter because of single inheritance.
Articulates the full trade-off (flexibility, encapsulation, runtime swapping, boilerplate) and ties it to favor-composition-over-inheritance, citing JDK behavior.
Reasons about adapter design at API boundaries — keeping the adapter surface minimal, avoiding adaptee API leakage, and choosing composition to keep modules loosely coupled and evolvable.
## Two ways to build an adapter An adapter must (1) present the **Target** interface to the client and (2) reach the **Adaptee** to do the real work. The difference between object and class adapters is purely in how step (2) is wired. ### Object adapter — composition The adapter **implements the Target interface** and **holds a reference to the adaptee** in a field. Each Target method delegates to one or more adaptee calls. ```java interface Target { String fetch(); } class Adaptee { String specificRequest() { return "data"; } } class ObjectAdapter implements Target { private final Adaptee adaptee; // composition ObjectAdapter(Adaptee adaptee) { this.adaptee = adaptee; } public String fetch() { return adaptee.specificRequest(); } // delegate } ``` ### Class adapter — inheritance The adapter **extends the adaptee class** and **implements the Target interface**, so it directly inherits the adaptee's methods and can call them via `super`. ```java class ClassAdapter extends Adaptee implements Target { public String fetch() { return specificRequest(); } // inherited } ``` ## Why Java prefers the object adapter Java has **single inheritance of implementation**: a class may `extends` exactly one class. That single constraint drives the trade-off: 1. **Inheritance slot** — the class adapter consumes your one `extends`. If your adapter already needs to extend something else (or you'd rather keep that slot free), you can't. 2. **Adaptee given as an interface or supertype** — composition lets you accept `InputStream` and adapt *any* concrete subtype passed in; the class adapter is bolted to one concrete class at compile time. 3. **Runtime flexibility** — an object adapter can be re-pointed at a different adaptee, or adapt several adaptees at once; a class adapter's identity is fixed. 4. **Encapsulation** — the class adapter inadvertently exposes all of the adaptee's public methods (it *is* an Adaptee), which can leak API and violate the Target's intended surface. The object adapter exposes only the Target. ### Where the class adapter wins (narrow) - No forwarding boilerplate — you inherit the methods. - You can **override** specific adaptee methods to tweak behavior. - Slightly less indirection. These rarely outweigh the flexibility cost, so idiomatic Java — and the JDK itself — almost always uses object adapters (composition). `InputStreamReader` extends `Reader` (the Target) but **holds** an `InputStream` internally rather than extending it, precisely so it can wrap any `InputStream`. ## Connection to a broader principle This mirrors the general 'favor composition over inheritance' guidance: inheritance is rigid and couples you to a concrete superclass, while composition (delegation) stays flexible and keeps interfaces narrow.
- Can you write a class adapter that extends two adaptees in Java?No. Java allows extending only one class, so a class adapter can subclass at most one adaptee. To combine several adaptees you must use composition (an object adapter holding multiple fields).
- What is one drawback of the class adapter beyond the inheritance limit?It exposes the adaptee's entire public API to clients (because it IS the adaptee), leaking surface the Target never intended and weakening encapsulation.
saying these in an interview costs you the question
- Claiming Java commonly uses class adapters — single inheritance makes the object adapter the default.
- Saying the object adapter extends the adaptee — it holds it as a field instead.
- Believing a class adapter can extend multiple adaptees.