I've been playing with generics in the latest JDK (among other features) to see how many of the features are useful in my eternal JavaSauce project. Quite a few as it happens. Annotations, varargs, new for loop iteration, static imports. And generics. In particular generics for the Visitor Pattern. Here's what I had before Tiger (some class names changed to save typing):
public interface Visitable
{
void accept(Visitor visitor);
}
public interface Visitor
{
void visit(A visited);
void visit(B visited);
void visit(C visited);
}
These interfaces allow me to visit the A, B, and C classes which all live in the same package and particpate in a Composite relationship (as is often the case). Given the recursive whole-part relationships there's a strong temptation to make the return type boolean instead of void. However, some concrete Visitors don't need or use a return type so for these classes the void return type is preferable. There's a definite tension. Generics can help. Here's what I've now got:
public interface Visitable
{
<R> R accept(Visitor<R> visitor);
}
public interface Visitor<R>
{
R visit(A a);
R visit(B b);
R visit(C c);
}
One slight problem with this approach is that R can only be a reference type:
public final class SomeVisitor
implements
Visitor<boolean> // compile time error
{ ...
}
A small Adapter can help to solve this:
public final class BooleanVisitorAdapter
implements
Visitor<Boolean>
{
public BooleanVisitorAdapter(final BooleanVisitor adaptee)
{
this.adaptee = adaptee;
}
public Boolean visit(final A visited)
{
return Boolean.valueOf(adaptee.visit(visited));
}
public Boolean visit(final B visited)
{
return Boolean.valueOf(adaptee.visit(visited));
}
public Boolean visit(final C visited)
{
return Boolean.valueOf(adaptee.visit(visited));
}
private final BooleanVisitor adaptee;
}
public interface BooleanVisitor
{
boolean visit(A visited);
boolean visit(B visited);
boolean visit(C visited);
}
Allowing:
someObject.accept(new BooleanVisitorAdapter(
new BooleanVisitor()
{
public boolean visit(final A visited) { ... }
public boolean visit(final B visited) { ... }
public boolean visit(final C visited) { ... }
}));
Note how the adapter creates a Boolean from a boolean. That could be handy; it could avoid the client writing
new Boolean(true)
which would cause needless pressure on the garbage collector (remember this is in a recursive context).