Generics and the visitor pattern

馋奶兔 提交于 2019-12-07 15:24:40

问题


I am having a problem with the Visitor pattern and generics. I have some abstract class whose children are to be visited. Look at this code:

public abstract class Element extends SomeSuperClass {
    public void accept(Visitor<? extends Element> v) {
        v.visit(this);
    }
}

public interface Visitor<T extends SomeSuperClass> {
    void visit(T element);
}

So the idea is: I have some class hierarchy (e.g. Element is a subclass of SomeSuperClass). I have got some generic Visitor interface to visit this hierarchy. Now in the middle of this hierarchy is the Element class, which is abstract and has it's own subclasses.

Now I want Element to accept all visitors of its subclasses, which is why I put this line:

public void accept(Visitor<? extends Element> v)

But now I am receiving error:

The method visit (capture#1-of ? extends Element) in the type Visitor<capture#1-of ? extends Element> is not applicable for the arguments (Element).

I understand that ? extends Element is not Element. My question is: Can I express my idea in a different way? Or I have just missed the idea of generics in this case?


回答1:


Note that T in <T extends SomeSuperClass> can be a type completely unrelated to Element and the compiler must ensure for the general case that visit(T t) will work for every possible T.

The code you have calls Visitor.visit(Element e), but the visitor in question could be Visitor<SubElement>. That wouldn't make sense.

I think that the requirement "Element must accept all visitors of its subclasses" doesn't make sense: the visitor must at least be able to visit Element and all its subclasses. That would be a Visitor<Element>.

The construct accept(Visitor<? extends Element> v) means that v can be any such Visitor<T> that T extends Element. It does not mean that the visitor itself will be of the type Visitor<? extends Element>. In fact, no such thing even exists in Java. Every Visitor will have a specific type parameter associated with it, not a wildcard.




回答2:


That cannot work - the visitors of ? extends Element may need to be able to access data (attributes / methods, ...) that Element doesn't have or know about.

You can't get a visitor that's supposed to visit objects that extends Element to necessarily be able to visit something that's a straight Element or even another, completely separate, subclass of Element.




回答3:


I don't think that what you're trying to do makes much sense. Making Visitor generic is useless: the accept() method must take a specific visitor interface as argument, so that subclasses of Element may be able to call specific overloads of visit().

interface Visitor {
  void visit(Element e);
  void visit(SubElement e);
}

class Element {
  public void accept(Visitor v) {
    v.visit(this);
  }
}

class SubElement { 
  public void accept(Visitor v) {
    v.visit(this);
  }
}

class ElementVisitor implements Visitor {
  public void visit(Element e) {}
  public void visit(SubElement e) {}
}

Note that the Visitor interface must be aware of all the classes in the Element hyerarchy that need a custom visit() implementation.




回答4:


The most general way to write it would be:

public void accept(Visitor<? super Element> v) {
    v.visit(this);
}

That way, even a Visitor<Object> would work (why shouldn't it).

Remember PECS (producer extends, consumer super). The visitor is a consumer, so it should be super.



来源:https://stackoverflow.com/questions/12109698/generics-and-the-visitor-pattern

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!