What you described meansHere is a straightforward solution, expressing the case literally to: implement ElementA.accept in terms of the base class Visitor, and ElementB.accept in terms of Visitor2:
public ElementA implements Visitable {
public void accept(Visitor v) {
//...
}
}
public ElementB { // no derivation from Visitable here, which would require
// to provide accept(Visitor v)
public ElementB {
public void accept(Visitor2 v) {
//...
}
}
This avoids the issue of two very similar ElementA.accept methods, which leads to the extendability problem mentioned in your question.
However, this solution will not work if the intention is to have some generic code which calls visitable.accept(someVisitor) without knowing beforehand if visitable is of type ElementA or ElementB (both derived from Visitable), and also not knowing if someVisitor is of type Visitor1 or Visitor2 (both derived from Visitor) . If that's the case, there is no way to prevent getting a combination of objects Visitor1 with ElementB at runtime without loosing the genericity. Hence you need to decide how you want the calling code to behave in case someone passes the forbidden combination in there:
maybe you want the behaviour from solution 1, where the forbidden combination throws an exception (so the caller can react accordingly)
maybe you want nothing to happen - similar to solution 1, but with
Vistor1.visit(ElementB element)implemented empty (so the caller does not have to care for)or maybe you prefer todon't need any generic caller. Then you can give up the genericity, make one function which calls
ElementA.acceptwith arbitrary visitors, and a second one which callsElementB.acceptonly with visitors of typeVisitor2(using my suggestion from above).
So look at the requirements of the caller - ask yourself: how will you are going to use the visitors (why do you need them at all), and which degree of genericity are you trying to achieve?