1

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

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

但是我收到泛型错误,并且不确定它是否表示设计错误,或者只是 java 泛型无法为我做的事情。这个想法似乎是有效的,我只想将 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() 方法提供了一个相似的类型,这样它们就有相同的类型签名,

公共组添加(T comp){ ... }

但这同样失败:

 Multiple markers at this line

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

我在这里错过了什么吗???TIA

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

4

2 回答 2

3

如果您的问题是为什么会出错,原因很简单: TinArea可以是无数Business个子类,而BusinessinGroup是一个非常具体的类。两种方法都不一样。编译器在执行类型检查时主要使用完整类型(即使它还考虑对某些类型的警告和特殊情况进行擦除)。这就是泛型的全部意义所在。否则,最好恢复为预泛型代码样式(仍受支持)。

如果不是,那么我不明白这个问题。但让我再补充几条评论:

1)继承描述了“是一种”关系,而不是“拥有”或“由……组成”关系。在代码中,您说 aGroup是一种Business,而 anArea是一种Group。对我来说,认为 aGroup 属于a Business(即 aBusiness 拥有 Groups)更为自然。Groups 和s 也一样Area。这两种关系都不是继承的,而是组合的。

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

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

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

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

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

这会创建代码重复(add每个类中的方法),在更现实的情况下可能会更高(您还可能需要方法count、、、listretrieve)。

于 2013-08-16T03:55:49.250 回答
1

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

public Group add(Business comp)

  1. 由于 T 无论如何都必须扩展业务,我不明白为什么这是一个问题,如果它只是摆脱覆盖注释的话。

  2. 您可能会将覆盖与重载混淆,即签名不同但名称相同。

  3. 从技术上讲,@Override注释没有任何功能,它只是为了捕捉程序员错误,所以如果它不编译,就把它去掉

于 2013-08-16T03:48:56.303 回答