【问题标题】:Generics and the visitor pattern泛型和访问者模式
【发布时间】:2012-08-24 12:47:35
【问题描述】:

我遇到了访问者模式和泛型的问题。我有一些抽象类要访问其孩子。看这段代码:

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);
}

所以想法是:我有一些类层次结构(例如ElementSomeSuperClass 的子类)。 我有一些通用的Visitor 接口来访问这个层次结构。现在在这个层次结构的中间是Element 类,它是抽象的并且有它自己的子类。

现在我希望Element 接受其子类的所有访问者,这就是我放这行的原因:

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

但现在我收到错误消息:

Visitor&lt;capture#1-of ? extends Element&gt; 类型中的方法访问 (capture#1-of ? extends Element) 不适用于参数 (Element)。

我了解? extends Element 不是Element。我的问题是:我可以用不同的方式表达我的想法吗?或者我只是在这种情况下错过了泛型的想法?

【问题讨论】:

  • 这是编译器错误还是运行时错误?
  • @RomanC 这绝对是编译时错误。

标签: java generics visitor-pattern


【解决方案1】:

这行不通 - ? extends Element 的访问者可能需要能够访问 Element 没有或不知道的数据(属性/方法,...)。

您无法让本应访问扩展 Element 的对象的访问者必然能够访问直接的 Element 甚至是另一个完全独立的 Element 子类。

【讨论】:

    【解决方案2】:

    我认为您尝试做的事情没有多大意义。使Visitor 泛型是没有用的:accept() 方法必须将特定的访问者接口作为参数,以便Element 的子类可以调用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) {}
    }
    

    请注意,Visitor 接口必须了解 Element 层次结构中需要自定义 visit() 实现的所有类。

    【讨论】:

      【解决方案3】:

      请注意,&lt;T extends SomeSuperClass&gt; 中的T 可以是与Element 完全无关的类型,并且编译器必须确保在一般情况下visit(T t) 将适用于所有可能的T

      您的代码调用Visitor.visit(Element e),但有问题的访问者可能是Visitor&lt;SubElement&gt;。那没有意义。

      我认为“Element 必须接受其子类的所有访问者”的要求没有意义:访问者必须至少能够访问Element 及其所有子类。那将是Visitor&lt;Element&gt;

      构造accept(Visitor&lt;? extends Element&gt; v) 意味着v 可以是Visitor&lt;T&gt;T extends Element 中的任何一个。它意味着访问者本身将属于Visitor&lt;? extends Element&gt; 类型。事实上,Java 中甚至不存在这样的东西。每个访问者都有一个与之关联的特定类型参数,而不是通配符。

      【讨论】:

      • 但是 s why I wrote method public void accept(Visitor extends Element> v)` 以确保访问者不能是 Visitor&lt;SubElement&gt;。无论如何,我有一个想法,我想要太灵活了。谢谢。
      • 我想你误解了语义,那么。 accept(Visitor&lt;? extends ELement&gt;) 正是 允许 Visitor&lt;SubElement&gt; 的构造。它允许任何Visitor&lt;T&gt;,例如T extends Element
      • 是的,我终于明白了。
      • 嗨@MarkoTopolnik,如果您有几分钟的时间,请看一下我的相关问题stackoverflow.com/questions/31869706/…。谢谢。
      【解决方案4】:

      最通用的写法是:

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

      这样,即使是 Visitor&lt;Object&gt; 也可以工作(为什么不应该)。

      记住PECS(生产者extends,消费者super)。访问者是消费者,所以应该是super

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-14
        • 2022-07-10
        • 1970-01-01
        • 1970-01-01
        • 2022-12-04
        • 1970-01-01
        相关资源
        最近更新 更多