我正在使用泛型来限制复合层次结构中的子类型,这样我就可以在层次结构的不同层强制使用不同的类型。
例如,具有(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将始终允许使用子类型。答案 0 :(得分:3)
如果您的问题是错误的原因,原因很简单:T
中的Area
可能是Business
子类的无限,Business
位于Group
是一个非常具体的类。两种方法都不相同。编译器在执行类型检查时主要使用完整类型(即使它还考虑了某种警告和特殊情况的删除)。这是仿制药的全部要点。否则,最好还原为pre-generics代码样式(仍然支持)。
如果没有,那么我不明白这个问题。但是,让我再补充几点意见:
1)继承描述“是一种”关系,而不是“有”或“由......组成”的关系。在代码中,您说Group
是一种Business
,Area
是一种Group
。对我而言,认为Group
属于至Business
更为自然(即Business
{{1 }} S)。与Group
和Group
相同。这两种关系都不是继承关系,而是组成关系。
2)在Area
中定义方法class Group<T extends Business>
并在add(T comp)
中定义方法class Area<T extends Business>
时,您说add(T comp)
和Group::add
相同的签名。那不对。组中的通用参数Area:add
是完全独立的区域中的T
。为什么?假设T
和B1
是商务的子类,而不是彼此的子类。没有人可以说B2
和Area<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 ;
}
}
)(您可能还需要方法add
,count
,list
等等。)
答案 1 :(得分:1)
覆盖是指当您拥有相同的签名时,您的签名会有所不同,因此它不是有效的覆盖。你的方法签名应该是。
public Group add(Business comp)
由于T必须扩展业务,我无法理解为什么这是一个问题,如果它只是摆脱覆盖注释。
您可能会因为重载超载而感到困惑,这就是签名不同但名称相同的情况。
从技术上讲,@Override
注释没有任何功能,它只是为了捕获程序员错误,所以如果它没有编译只是摆脱它