Show a forwarding wrapper class — the classic Instrumente… — Cracked Java
// Object-Oriented Programming · Composition vs Inheritance
MidCoding

Show a forwarding wrapper class — the classic InstrumentedSet example.

The pattern has three layers. A reusable ForwardingSet<E> that delegates every Set method to a wrapped instance, an application-level InstrumentedSet<E> that extends ForwardingSet and adds the counting behavior, and the wrapped Set<E> itself — which can be any implementation. The decoupling is what makes it powerful: the same InstrumentedSet works over HashSet, TreeSet, LinkedHashSet, or a custom concurrent set.

Step 1 — the forwarding base

This is pure boilerplate, written once and reused:

public class ForwardingSet<E> implements Set<E> {
    private final Set<E> s;
    public ForwardingSet(Set<E> s) { this.s = s; }

    @Override public int size()                         { return s.size(); }
    @Override public boolean isEmpty()                  { return s.isEmpty(); }
    @Override public boolean contains(Object o)         { return s.contains(o); }
    @Override public Iterator<E> iterator()             { return s.iterator(); }
    @Override public Object[] toArray()                 { return s.toArray(); }
    @Override public <T> T[] toArray(T[] a)             { return s.toArray(a); }
    @Override public boolean add(E e)                   { return s.add(e); }
    @Override public boolean remove(Object o)           { return s.remove(o); }
    @Override public boolean containsAll(Collection<?> c)              { return s.containsAll(c); }
    @Override public boolean addAll(Collection<? extends E> c)         { return s.addAll(c); }
    @Override public boolean retainAll(Collection<?> c)                { return s.retainAll(c); }
    @Override public boolean removeAll(Collection<?> c)                { return s.removeAll(c); }
    @Override public void clear()                       { s.clear(); }
    @Override public boolean equals(Object o)           { return s.equals(o); }
    @Override public int hashCode()                     { return s.hashCode(); }
    @Override public String toString()                  { return s.toString(); }
}

Step 2 — the instrumenting wrapper

Now the interesting class is tiny:

public class InstrumentedSet<E> extends ForwardingSet<E> {
    private int addCount = 0;

    public InstrumentedSet(Set<E> s) { super(s); }

    @Override public boolean add(E e) {
        addCount++;
        return super.add(e);
    }
    @Override public boolean addAll(Collection<? extends E> c) {
        addCount += c.size();
        return super.addAll(c);
    }

    public int addCount() { return addCount; }
}

Step 3 — use it

Set<String> s = new InstrumentedSet<>(new TreeSet<>());
s.addAll(List.of("c", "a", "b"));
((InstrumentedSet<String>) s).addCount();   // 3, correctly

The wrapped TreeSet could equally be a HashSet, a LinkedHashSet, or a Set returned by some third-party library — InstrumentedSet doesn't care.

Why this beats extends HashSet

extends HashSet<E>:        wraps Set<E>:

InstrumentedHashSet         InstrumentedSet
      |                            |
      v                            v
   HashSet                    ForwardingSet
      |                            |
      v                            v
 <coupled to HashSet         <decoupled — any
  self-use bugs>              Set implementation>
Inheritance vs forwarding wrapper

The wrapper invokes only the public Set API. addAll's internal call to add happens inside the wrapped instance, where super.add doesn't reroute back to the wrapper — so the double-counting bug from the inheritance version disappears.

Mark your status