Card 05/ 09

ExampleDifficulty: Intermediate2 min

Factory Method in the Wild: Java's Collection.iterator()

Every collection hands back its own iterator, and the for loop never names the class.

Java's collections library uses Factory Method in a method most Java developers call daily without seeing it: iterator(). Collection declares it, each collection class overrides it to return its own iterator class, and the code that loops never learns which one it received. This card shows the concrete classes behind that one method.

The listing asks five collections for an iterator and prints the class of what comes back.

java
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;

public class FmIterators {
    public static void main(String[] args) {
        List<Collection<String>> collections = List.of(
            new ArrayList<>(List.of("a")),
            new LinkedList<>(List.of("a")),
            new HashSet<>(List.of("a")),
            new TreeSet<>(List.of("a")),
            new ArrayDeque<>(List.of("a")));

        for (Collection<String> collection : collections) {
            System.out.println(collection.getClass().getSimpleName()
                + " -> " + collection.iterator().getClass().getName());
        }
    }
}
OutputRan with eclipse-temurin:21-jdk (Java 21.0.12)
ArrayList -> java.util.ArrayList$Itr
LinkedList -> java.util.LinkedList$ListItr
HashSet -> java.util.HashMap$KeyIterator
TreeSet -> java.util.TreeMap$KeyIterator
ArrayDeque -> java.util.ArrayDeque$DeqIterator
Each collection returns a different class through the same method.

The five collections return five different iterator classes. The $ in a name such as ArrayList$Itr marks a class declared inside another, so Itr is a private class inside ArrayList. A HashSet stores its elements as the keys of a HashMap, which is why it hands back the map's key iterator. You could not create these classes yourself if you tried, and a for-each loop has no need to: it calls iterator() and uses the result as an Iterator.

The library classes in the pattern's roles
ParticipantIn the Java collections library
CreatorCollection (through Iterable), which declares iterator()
ConcreteCreatorArrayList, LinkedList, HashSet and the others
ProductIterator, the interface every result implements
ConcreteProductArrayList$Itr, LinkedList$ListItr and the other private classes
Factory methoditerator()

What this case shares with the notification sender is the shape, not the domain: a method on the creator returns a product through an interface, each subclass decides the concrete class, and the caller never names it. In the notifier the caller was the creator's own notify_shipped; here it is the for-each loop and any other code that holds a Collection.

The class names are implementation details of OpenJDK, as run here on Java 21, and could change in a later release without breaking any program, because no program names them.