ForwardingList
(which extends ForwardingCollection
, which in turn extends ForwardingObject
) implements the decorator pattern.
To use, you simply need to do two things:
@Override delegate()
to return the backing delegate instance that methods are forwarded to
@Override
whatever List
method you want/need to decorate
The decorator pattern allows you to use composition instead of inheritance (Effective Java 2nd Edition, Favor composition over inheritance), and ForwardingList
from Guava provides a convenient template from which to write your own List
implementation, providing all the plumbing mechanism for you.
Note that if you are planning to decorate an ArrayList
, you'd probably want your ForwardingList
subclass to also implement RandomAccess
.
Example: ListWithDefault
Here's an (incomplete!) example of a ForwardingList
that substitutes null
values in the delegate with a given default value.
import java.util.*;
import com.google.common.collect.*;
public class ListWithDefault<E> extends ForwardingList<E> {
final E defaultValue;
final List<E> delegate;
ListWithDefault(List<E> delegate, E defaultValue) {
this.delegate = delegate;
this.defaultValue = defaultValue;
}
@Override protected List delegate() {
return delegate;
}
@Override public E get(int index) {
E v = super.get(index);
return (v == null ? defaultValue : v);
}
@Override public Iterator<E> iterator() {
final Iterator<E> iter = super.iterator();
return new ForwardingIterator<E>() {
@Override protected Iterator<E> delegate() {
return iter;
}
@Override public E next() {
E v = super.next();
return (v == null ? defaultValue : v);
}
};
}
}
We can then test it as follows:
public static void main(String[] args) {
List<String> names = new ListWithDefault<String>(
Arrays.asList("Alice", null, "Bob", "Carol", null),
"UNKNOWN"
);
for (String name : names) {
System.out.println(name);
}
// Alice
// UNKNOWN
// Bob
// Carol
// UNKNOWN
System.out.println(names);
// [Alice, null, Bob, Carol, null]
}
Note that this is an incomplete implementation. The toString()
method still returns the delegate's toString()
, which isn't aware of the default value. A few other methods must be @Override
as well for a more complete implementation.