I was going to mention the unchecked wrapped re-throw approach, but Giodude beat me to it. Instead, I'll suggest another approach that I call the courtesy exception (because it is integrated in the interfaces as a courtesy to implementers).
When designing the visitor and Mammal interfaces, I outfit them to handle one exception of the user's choosing. The visitor:
public interface MammalVisitor<T extends Throwable>
{
void visit(Cat m) throws T;
void visit(Dog m) throws T;
void visit(Cow m) throws T;
}
And Mammal:
public interface Mammal
{
<T extends Throwable> void accept(MammalVisitor<T> visitor) throws T;
}
And the implementation of Mammal:
public class Cat implements Mammal
{
@Override
public <T extends Throwable> void accept(MammalVisitor<T> visitor) throws T
{
visitor.visit(this);
}
}
Dog and Cow are implemented identically. And the printing visitor:
public class MammalPrinter implements MammalVisitor<IOException>
{
private final Appendable out;
public MammalPrinter(Appendable out)
{
this.out = out;
}
@Override
public void visit(Cat m) throws IOException
{
out.append("I'm a cat");
}
@Override
public void visit(Dog m) throws IOException
{
out.append("I'm a dog");
}
@Override
public void visit(Cow m) throws IOException
{
out.append("I'm a cow");
}
}
And usage:
Mammal m = MammalFactory.getMammal();
MammalPrinter mp = new MammalPrinter(System.out);
try
{
m.accept(mp);
}
catch (IOException e)
{
System.err.println("An IOException occurred");
}
Which results in a much more intuitive and easy to implement usage from the end-user's perspective.
With this pattern, if a visitor does not have a checked exception to throw, they specify some unchecked exception as the generic in their implementation:
public class MammalPrinter implements MammalVisitor<RuntimeException>
{
When Mammal.accept() is called with the above visitor, no catching is needed to be syntactically correct. Perhaps you could further increase readability by making an extend of RuntimeException called "NeverThrown" that has a private constructor.