具有分层复合模式的Java泛型错误

时间:2013-08-16 03:46:48

标签: java generics composite

我正在使用泛型来限制复合层次结构中的子类型,这样我就可以在层次结构的不同层强制使用不同的类型。

例如,具有(Business:Division:Department:Group:Person)层次结构,其中每个级别的复合节点仅接受正确类型的子节点(层次结构中的下一个较低级别)。因此,我将在实例化的每个级别类型中使用泛型构造复合中的级别,以仅接受来自下一级别的节点。

但是我在泛型中遇到错误,并且不确定它是否表示设计错误,或者只是java Generics无法为我做的事情。这个想法似乎有效,我只想限制add()方法接受的类型,只接受层次结构中特定下一级的子类型来强制结构级别。由于我只是在每个阶段降低类型的上限,所以发送到列表的所有消息仍然有效。

典型GOF-Composite模式中的顶级节点:

   public abstract class Business { ... }

复合节点层次结构顶级:

  public abstract class Group extends Business {

    protected List<Business> subs;      // composite links
    public Group add(Business comp) {
        subs.add(comp);
        return this;
    }

复合层次结构中的后续级别:

 public class Area<T extends Business> extends Group {

    protected List<Business> subs;      // composite links

    public Area(String title){
        super(title);
        subs = new ArrayList<Business>();
    }

    @Override
    public Group add(T comp) {    // ** Error Here **
        super.add(comp);
        return this;
    }

错误是:

 Multiple markers at this line
    - The method add(T) of type Area<T> must override a superclass method
     

- 名称冲突:类型为Area的方法add(T)与Group类型的add(Business)具有相同的删除权但不覆盖它

我尝试了一个变体,我给了Group :: add()方法类似的类型,所以它们将具有相同的类型签名,

public group add(T comp){...}

但是同样失败了:

 Multiple markers at this line
     

- Area类型的方法add(T)必须覆盖超类方法      - 覆盖labs.composite.company.Group.add      - 名称冲突:类型为区域的方法add(T)与Group类型的add(T)具有相同的擦除但不覆盖它

我在这里遗漏了什么? TIA

PS:实际上我认为这种泛型的使用并不能完全符合我的要求(即使它确实有效!),因为我不仅要改变每个级别的上限,还要求特定的单个类型层次结构中的级别,而不是任何协变参数类型。我真的想要这样的东西,“对于任何类型的T,它是Composite的子类型,在add()方法中只接受那种类型的对象”,我认为相反,泛型说“ 接受任何对象作为参数,它是Composite 的子类型。我想这是不可能的,因为Java中的参数是协变的,LSP将始终允许使用子类型。

2 个答案:

答案 0 :(得分:3)

如果您的问题是错误的原因,原因很简单:T中的Area可能是Business子类的无限,Business位于Group是一个非常具体的类。两种方法都相同。编译器在执行类型检查时主要使用完整类型(即使它还考虑了某种警告和特殊情况的删除)。这是仿制药的全部要点。否则,最好还原为pre-generics代码样式(仍然支持)。

如果没有,那么我不明白这个问题。但是,让我再补充几点意见:

1)继承描述“是一种”关系,而不是“有”或“由......组成”的关系。在代码中,您说Group是一种BusinessArea是一种Group。对我而言,认为Group 属于Business更为自然(即Business {{1 }} S)。与GroupGroup相同。这两种关系都不是继承关系,而是组成关系。

2)在Area中定义方法class Group<T extends Business>并在add(T comp)中定义方法class Area<T extends Business>时,您说add(T comp)Group::add相同的签名。那不对。组中的通用参数Area:add完全独立的区域中的T。为什么?假设TB1是商务的子类,而不是彼此的子类。没有人可以说B2Area<B1>:add()具有相同的签名。事实上,这种等价性的唯一情况是泛型参数是相同的(即Group<B2>:add()Area<B1>)。当等效性仅在少数特定情况下(代码未另外描述)时,Java不能认为签名等效。

3)GOF的复合设计模式不适用于这种情况,因为它不代表分层组合,而是我们可以称之为“不受限制”的组合。根据此模式,Group<B1>可以包含aComposite,但这些differentComponents可以是任何类型的Component,无论类层次结构中有多高或多低。你想要的是Composite成为紧迫的下级。不多也不少。

我不记得曾将此案例视为设计模式。可能是因为它太简单了。请参阅下一点中的最后一个变体4)。

4)您的代码应该采用以下形式:

differentComponents

等等。如果您允许方法public class Grouping<C,T> { C curr; List<T> subs; public C add(T comp) { this.subs.add(comp); return this.curr ; } } public class Business extends Grouping<Business,Group> { // Grouping::add does not need to be overriden } public class Group extends Grouping<Group,Area> { // Grouping::add does not need to be overriden } 返回add而不是void,则可以删除所有类中的泛型参数:

C

如果你想让它变得非常简单(但也许不那么强大):

public class Grouping<T> {
    List<T> subs;
    public void add(T comp) {
        this.subs.add(comp);
    }
}

public class Business extends Grouping<Group> {
    // Grouping::add does not need to be overriden
}
public class Group extends Grouping<Area> {
    // Grouping::add does not need to be overriden
}

这会在更现实的情况下创建更复杂的代码重复(每个类中的方法public class Business { List<Group> subs; public Business add(Group comp) { this.subs.add(comp); return this ; } } public class Group { List<Area> subs; public Group add(Area comp) { this.subs.add(comp); return this ; } } )(您可能还需要方法addcountlist等等。)

答案 1 :(得分:1)

覆盖是指当您拥有相同的签名时,您的签名会有所不同,因此它不是有效的覆盖。你的方法签名应该是。

public Group add(Business comp)

  1. 由于T必须扩展业务,我无法理解为什么这是一个问题,如果它只是摆脱覆盖注释。

  2. 您可能会因为重载超载而感到困惑,这就是签名不同但名称相同的情况。

  3. 从技术上讲,@Override注释没有任何功能,它只是为了捕获程序员错误,所以如果它没有编译只是摆脱它