Card 05/ 08

ExampleDifficulty: Intermediate2 min

Adapter in the Wild: Java's InputStreamReader and Arrays.asList

A byte stream read as characters, and an array used as a List.

Java's standard library contains adapters in plain sight, and this card shows two of them. In both, a client is written against one interface and has an object with another. InputStreamReader lets code that reads characters, through Reader, read from an InputStream of bytes. Arrays.asList lets code that works with a List use an array.

java
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

public class AdJdk {
    public static void main(String[] args) throws IOException {
        InputStream bytes = new ByteArrayInputStream("café".getBytes(StandardCharsets.UTF_8));
        System.out.println("bytes available: " + bytes.available());
        Reader reader = new InputStreamReader(bytes, StandardCharsets.UTF_8);
        int count = 0;
        StringBuilder text = new StringBuilder();
        for (int c = reader.read(); c != -1; c = reader.read()) {
            text.append((char) c);
            count++;
        }
        System.out.println("characters read: " + count + " (" + text + ")");

        String[] colours = {"red", "green", "blue"};
        List<String> view = Arrays.asList(colours);
        view.set(0, "crimson");
        System.out.println("array after set on the list: " + colours[0]);
        try {
            view.add("violet");
        } catch (UnsupportedOperationException e) {
            System.out.println("add: " + e.getClass().getName());
        }
    }
}
OutputRan with eclipse-temurin:21-jdk (Java 21.0.12)
bytes available: 5
characters read: 4 (café)
array after set on the list: crimson
add: java.lang.UnsupportedOperationException
Five bytes read as four characters, and a list that writes through to its array but cannot grow.
The participants in each JDK adapter
ParticipantReading bytes as charactersUsing an array as a list
TargetReaderList
AdapterInputStreamReaderthe list Arrays.asList returns
AdapteeInputStreamthe array

The word "café" is five bytes in UTF-8, because "é" takes two, and the reader returns four characters: the adapter's translation is decoding, which the stream of bytes cannot do. The class's name lists the adaptee first and the target second, which is a common way to misread it; the class is a Reader, made from an InputStream.

Arrays.asList returns a list that holds the array rather than a copy, so set on the list changes the array, and the output shows "crimson" in colours[0]. It also shows the limit of the adaptation: an array cannot grow, so add throws UnsupportedOperationException. An adapter can only offer what the adaptee can do, and a target method it cannot honour has to fail.