skip to content

Adapter in Java

Adapter in the JDK: Arrays.asList presenting an array as a List, and InputStreamReader turning bytes into characters. Interviewers ask about object versus class adapters, which in Java means composition versus inheritance.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What is the Adapter pattern, and how does it appear in the JDK?

level: juniorimportance: must knowfreq 70%

answer

  1. Target / Adaptee / Adapter / Client roles
  2. InputStreamReader: bytes -> chars
  3. Arrays.asList: array -> List view
  4. Object adapter = composition (Java's default)
  5. Adapter changes interface; Decorator keeps it

basics

~20 s

Adapter is a wrapper that lets two incompatible interfaces work together. It takes an object with one interface and exposes it as another. In the JDK, InputStreamReader adapts a byte InputStream into a char Reader.

solid answer

~40 s

The Adapter pattern converts the interface a class offers into the interface a client expects, so two otherwise-incompatible types can collaborate. You wrap the existing object (the adaptee) in an adapter that implements the target interface and forwards calls, translating between the two. It is a structural pattern used when you can't change either side, for example when reusing legacy or third-party code. In the JDK, InputStreamReader adapts a byte-oriented InputStream to the char-oriented Reader interface, decoding bytes via a charset; Arrays.asList adapts a backing array to the List interface. The key intent is reuse-without-modification: the client codes only against the target interface and stays unaware of the adaptee.

go deeper

for a junior

Can define Adapter as a wrapper that makes incompatible interfaces work together and name InputStreamReader as a JDK example.

for a middle

Names the Target/Adaptee/Adapter roles, explains InputStreamReader (bytes->chars) and Arrays.asList (array->List view), and distinguishes Adapter from Decorator.

for a senior

Contrasts object vs class adapter, explains why composition dominates in Java, and reasons about the open/closed benefit and the view-semantics gotchas of Arrays.asList.

for a principal

Frames Adapter as an integration/anti-corruption tool at module boundaries, weighs adapter proliferation vs interface redesign, and connects it to API evolution and dependency-inversion strategy.

## The problem Sometimes you have an object that does what you need, but its **interface** (the set of methods it exposes) does not match the interface your code is written against. You can't (or don't want to) edit either side — maybe one is a third-party library, maybe it is legacy code many callers depend on. An **Adapter** is a small class that sits between them and translates. ## The roles - **Target** — the interface the client expects to call (e.g. `Reader`, `List`). - **Adaptee** — the existing object whose interface doesn't match (e.g. `InputStream`, a raw array). - **Adapter** — implements (or extends) the Target and holds/uses the Adaptee, forwarding each Target call to the appropriate Adaptee call, converting data as needed. - **Client** — code that depends only on the Target; it never sees the Adaptee. Analogy: a travel **power-plug adapter** lets a device with a European plug fit a UK socket. Neither the device nor the socket changes; the adapter bridges the shape mismatch. ## JDK examples **`InputStreamReader`** — an `InputStream` produces raw **bytes**; a `Reader` produces **chars**. They are incompatible interfaces. `InputStreamReader extends Reader` and wraps an `InputStream`, decoding the byte stream into characters using a charset (e.g. UTF-8). So `new InputStreamReader(socket.getInputStream(), UTF_8)` lets character-based code consume a byte stream. ```java Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); ``` **`Arrays.asList(T...)`** — turns a Java array into something usable where a `List` is expected. It returns a fixed-size `List` **view** backed by the same array: writes to the list write through to the array, and `set` works, but structural changes (`add`/`remove`) throw `UnsupportedOperationException`. It adapts the array's interface (indexed access) to the `List` interface. ## Object adapter vs class adapter There are two ways to build an adapter: - **Object adapter (composition)** — the adapter *holds a reference* to the adaptee as a field and delegates to it. This is the dominant Java form because Java has single inheritance of classes, and composition is more flexible (you can adapt any subtype of the adaptee, swap it at runtime). - **Class adapter (inheritance)** — the adapter *extends* the adaptee and *implements* the target. In Java this only works when the adaptee is a class you can subclass and the target is an interface, because you cannot extend two classes. `InputStreamReader` is close to this style: it `extends Reader` (target) and is *constructed with* an `InputStream` (adaptee, held internally) — so even it leans on composition for the adaptee. ## Related patterns (don't confuse) - **Decorator** keeps the *same* interface and *adds behavior* (e.g. `BufferedReader` wraps a `Reader` and stays a `Reader`). Adapter *changes* the interface. - **Facade** simplifies a whole subsystem behind one new interface; Adapter targets one specific incompatible interface. ## Why it matters Adapter is the practical tool for integrating mismatched code without rewriting it, honoring the open/closed principle (extend behavior without modifying existing classes).

  • How does Adapter differ from Decorator?
    Adapter changes the interface so two incompatible types can talk; Decorator keeps the same interface and adds behavior. BufferedReader wrapping a Reader is a Decorator (still a Reader); InputStreamReader wrapping an InputStream is an Adapter (InputStream -> Reader).
  • Why is Arrays.asList only a partial List?
    It returns a fixed-size view backed by the original array, so set works (writes through to the array) but add/remove throw UnsupportedOperationException because an array can't change size.

A travel power-plug adapter: it doesn't change the device or the wall socket, it just bridges the shape mismatch so they can connect.

saying these in an interview costs you the question

  • Calling InputStreamReader a Decorator — it changes the interface (bytes to chars), so it's an Adapter.
  • Thinking Arrays.asList returns a fully mutable, growable ArrayList.
  • Saying Adapter modifies the adaptee's source code — the whole point is reuse without modification.

context

open as a page

How does Arrays.asList illustrate the Adapter pattern, and what surprising behaviors come from its view semantics?

level: middleimportance: should knowfreq 50%

basics

~20 s

Arrays.asList wraps an array in the List interface without copying it. The list is a fixed-size view backed by the array: changing the list changes the array and vice versa, set works, but add and remove throw UnsupportedOperationException.

open as a page

What is the difference between an object adapter and a class adapter in Java, and which does Java favor?

level: middleimportance: should knowfreq 55%

basics

~20 s

An 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.

open as a page

How do Adapter, Decorator, Proxy, and Facade differ, and how does InputStreamReader vs BufferedReader illustrate the Adapter/Decorator distinction?

level: seniorimportance: should knowfreq 48%

basics

~20 s

All four wrap an object, but for different reasons. Adapter changes the interface; Decorator keeps the interface and adds behavior; Proxy keeps the interface and controls access; Facade hides a subsystem behind a simpler interface. InputStreamReader is an Adapter (bytes to chars); BufferedReader is a Decorator (still a Reader, adds buffering).

open as a page

When should you introduce an Adapter at a module or system boundary rather than changing the interface directly, and what are the design trade-offs?

level: principalimportance: nice to knowfreq 32%

basics

~20 s

Use an Adapter when you can't or shouldn't change either side — third-party libraries, legacy code, or stable public APIs — to keep your code depending on your own clean interface instead of the foreign one. The cost is an extra layer to maintain and translate.

open as a page