Reputation: 2071
I have the following setup defined as follows:
public interface Element {
public <R> R accept(Visitor<R> visitor);
}
public interface Visitor<R> {
}
class SpecificElement implements Element {
@Override // ERROR: didn't override accept in parent
public <R> R accept(SpecificVisitor visitor) {
return visitor.visitSpecificElement(this);
}
}
class SpecificVisitor implements Visitor<Boolean> {
public Boolean visitSpecificElement(SpecificElement element) {
return true;
}
}
How can I change the setup above such that I can require subtype SpecificElement
to only accept SpecificVisitor
and able to override parent class' accept
method.
What I want to happen is to mandate that the implementing class SpecificElement
will only accept visitors of type SpecificVisitor
and nothing else, but still keep the signature of Element
's type interface at Visitor
.
UPDATE 1: I added the type parameter V extends Visitor<?>
in Element
, then extended it with SpecificElement extends Element<SpecificVisitor<?>
and was able to get what I want (stricter parameter types in method accept): <R> R accept(SpecificVisitor<?> visitor)
. However my problem now is that I want to determine the return type of the method accept
based on the type parameter of the argument visitor (in this case ?
). Changing ?
to R
causes compile error.
public interface Element<V extends Visitor<?> {
public <R> R accept(Visitor<R> visitor);
}
public interface Visitor<R> {
}
class SpecificElement implements Element<SpecificVisitor<?>> {
@Override
public <R> R accept(SpecificVisitor<R> visitor) { // error in type parameter R in SpecificVisitor
return visitor.visitSpecificElement(this);
}
}
class SpecificVisitor implements Visitor<Boolean> {
public Boolean visitSpecificElement(SpecificElement element) {
return true;
}
}
UPDATE 2: I would like to write a family of Visitors for a family of Elements without giving up type safety. Notice the use of same Visitor subtype for an Element subtree. For Example:
--- Element -> Visitor<R>
|
|---- SpecificElement -> SpecificVisitor<R>
| |
| |---------SpecificElementA -> SpecificVisitor<R>
| |
| |---------SpecificElementB -> SpecificVisitor<R>
|
|---- FancyElement -> FancyVisitor<R>
|
|---------FancyElementA -> FancyVisitor<R>
|
|---------FancyElementB -> FancyVisitor<R>
Upvotes: 0
Views: 1100
Reputation: 9152
This can be accomplished, as long as you know your subtypes you want to use in advance. (You're using the visitor pattern, so you need to know all the element types in advance, anyway.) The important thing is to build your Visitor
interface from the bottom up, not the top down:
public interface ElementVisitor<R> extends SpecificVisitor<R>, FancyVisitor<R> {
}
public interface Element {
public <R> R accept(ElementVisitor<R> visitor);
}
Now Element
subtypes will need a little boilerplate, so that they can implement the Element
interface:
public abstract class FancyElement implements Element {
public abstract <R> R accept(FancyVisitor<R> visitor);
public <R> R accept(ElementVisitor<R> visitor) {
return accept((FancyVisitor<R>) visitor);
}
}
And then we can define the specific subtype visitors and elements:
public class FancyElement1 extends FancyElement {
public <R> R accept(FancyVisitor<R> visitor) {
return visitor.visit(this);
}
}
public interface FancyVisitor<R> {
public R visit(FancyElement1 e);
// ...
}
… and repeat for SpecificVisitor
.
Now you can define either a FancyVisitor
that can only visit FancyElement
s, a SpecificVisitor
that can only visit SpecificElement
s, or an ElementVisitor
that can visit all Element
s.
Upvotes: 1
Reputation: 137
maybe this version will be usefull.
make Element interface generic
interface Element<V> {
<R> R accept(V visitor);
}
and SpecificElement should be so:
public class SpecificElement implements Element<SpecificVisitor> {
public <R> R accept(SpecificVisitor visitor) {
return visitor.visit();
}
}
note that Element_s generic type V is not strict type, there can be anything. implementor of Element interface, can write any class in it, even if it doesn't implement Visitor interface. this is the disadvantage of this code.
Upvotes: 0