【问题标题】:Method overloading not working as expected with Java generics使用 Java 泛型时,方法重载无法按预期工作
【发布时间】:2013-10-16 02:34:27
【问题描述】:

我有一个通用类:

public class Facet<C extends Component>
{
    private final C child;

    public Facet(C child) { this.child = child; }

    public C getChild() { return this.child; }

    public UIComponent getViewComponent() {
        return PrimeFacesComponentFactory.create(facesContext, this.getChild());
    }
}

我有一个组件工厂,它也有一堆方法,其中几个看起来像这样:

public static UIComponent create(FacesContext fc, Component component) {
    throw new UnsupportedOperationException("Method not yet implemented for class: " + component.getClass().getName());
}

public static UIRow create(FacesContext fc, Row modelRow) { ... }

public static UIColumn create(FacesContext fc, Column modelColumn) { ... }

注意:在每个工厂方法中,第二个参数是扩展 Component 的对象,返回的始终是 UIComponent 的子类。第一个工厂方法(引发异常的那个)旨在成为“包罗万象”,如果没有为特定组件编写特定的工厂方法,则会调用它。

我正在尝试做的事情:应该调用正确的工厂方法,具体取决于组件方面的子组件类型。发生的事情是第一个工厂方法总是被调用(例如,抛出异常的那个)。这对我来说是违反直觉的,因为 C 的类型在运行时是已知的,而 C 是“扩展组件的某种特定类型”。

调试,我在Facet#getViewComponent设置了一个断点,当我知道facet.getChild()会返回一个Row类型的对象时调用它。所以在这个例子中,C 是 Row。让我惊讶的是,当调用工厂方法时,例如

PrimeFacesComponentFactory.create(facesContext, facet.getChild());

程序执行流向“catch all”工厂方法!但是,如果我显式转换 facet.getChild() 如下:

PrimeFacesComponentFactory.create(facesContext, (Row)facet.getChild());

然后调用特定的方法。很奇怪,因为我只是/冗余地将一行转换为一行。

我读过some about why this is happening,但对我来说它仍然很模糊且违反直觉。无论如何,我该如何解决这个问题?

【问题讨论】:

  • 你能在调用create(..)之前显示facet的声明吗?还是您的意思是this.getChild()?
  • 哦,对了!我做了很多代码简化以使示例更简单,是的,而不是 facet.getChild() 它应该只是 this.getChild() 正如你所建议的那样。我会更新问题。

标签: java generics


【解决方案1】:

重载决议在编译时针对Facet 类发生一次,而不是针对Facet&lt;C&gt; 中的每个C,而不是在运行时。一个潜在的解决方案是让你的包罗万象的方法更智能:

public static UIComponent create(FacesContext fc, Component component) {
    if (component instanceof Row)
        return create(fc, (Row)component);
    else if (component instanceof Column)
        return create(fc, (Column)component);
    // include other types, and lastly
    else
        throw new UnsupportedOperationException("Method not yet implemented for class: " + component.getClass().getName());
}

【讨论】:

  • 好主意。但是,我希望没有那么多 if-else-if 的东西,因为有这么多组件。也许使用访客...
  • visitor 的优势在于更少(如 0 次)演员,但肯定不会更少。
  • @JudgeMental 如果我使用访问者,我将能够从“catch all”方法中删除所有 if/else-ifs,这样会减少行数。
  • 好吧,你需要一个Visitor 接口,每个子类中都有一个accept( Visitor ) 方法,并且你的if/else-if 子句将成为新类中Visitor 接口方法的实现.我很想知道如何更简洁地实现这一点。
  • @JudgeMental 你是对的。在我下次发言之前,我应该仔细考虑并编写一些代码 :) 我确实写了访问者,正如你所说的那样,它消除了对演员表的需要。另一个好处:因为每个组件都必须实现accept(Visitor),当我将来向 API 添加新组件时,除非我更新访问者,否则代码不会编译,这确保不会抛出 UnsupportedOperationException。谢谢你的耐心。感谢您将我推向正确的方向。
【解决方案2】:

以下声明

public C getChild() { return this.child; }

public UIComponent getViewComponent() {
    return PrimeFacesComponentFactory.create(facesContext, facet.getChild());
}

假设facet 是this,在编译时,关于类型参数C 的唯一已知就是它扩展了Component。因此编译器将此方法调用与声明为接受Component 的方法绑定。

public static UIComponent create(FacesContext fc, Component component) {
    throw new UnsupportedOperationException("Method not yet implemented for class: " + component.getClass().getName());
}

【讨论】:

  • 谢谢我更新了这个问题。 facet.getChild() 不正确。它应该是“this.child”。
  • @PatrickGarner 对。所以这就是它发生的原因。至于解决方案,请查看 Tim's。我可能会尝试一些东西,但现在没有时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 2013-04-05
  • 2017-04-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多