通用树,自有界generics

我要为我的一个项目添加通用性。 我喜欢generics,因为这会使我的代码更加健壮,自我记录并删除所有那些丑陋的演员。

然而,我遇到了一个棘手的案例,并试图表达我的一个结构的“递归”约束。

这基本上是某种“通用”树,具有双链接(对于孩子和父母)。 我最大限度地简化了课程以显示问题:

public class GenericTree< ParentClass extends GenericTree, ChildClass extends GenericTree> { // Attributes private ArrayList children = new ArrayList(); private ParentClass parent = null; // Methods public void setParent(ParentClass parent) { this.parent = parent; } public void addChild(ChildClass child) { child.setParent(this); this.children.add(child); } } 

问题出在指令: child.setParent(this)

Java给出以下错误:

绑定不匹配:ChildClass类型的方法setParent(?)不适用于
参数(GenericTree)。 通配符参数? 没有下限,实际上可能比参数GenericTree更具限制性

我想要的是能够表达如下:

 public class GenericTree< ParentClass extends GenericTree, ChildClass extends GenericTree> 

要说子类的父类应该是它自己…

我看了一些关于自我边界generics的文章,但我不知道如何在这种情况下应用它。

任何帮助,将不胜感激。

我能得到的最接近的是遵循Enum的自我参照模式。 它仍然需要一个未经检查的强制转换,但假设您的子类正确定义T,它应该是安全的。

 public class GenericTree, P extends GenericTree, C extends GenericTree> { // Attributes private ArrayList children = new ArrayList(); private P parent = null; // Methods public void setParent(P parent) { this.parent = parent; } public void addChild(C child) { @SuppressWarnings("unchecked") final T thisAsType = (T) this; child.setParent(thisAsType); this.children.add(child); } } 

编辑:实施示例

 public static class SingleTypeTree extends GenericTree, SingleTypeTree, SingleTypeTree> { } 

不要将generics用于非均匀树,使用接口和强制转换。

虽然你可以用generics来解决一些问题,但是生成的代码会很脆弱,显示你以前从未见过的错误消息,修复bug通常会导致尝试和错误,即使你得到编译,你也不会知道为什么 ;-)

[EDIT2]添加了addChild()和下面的用法示例。

[编辑]还在我身边吗? 如果您真的必须,请使用此API:

 interface ParentNode { List getChildren(); void addChild(Child child); } interface ChildNode { void setParent(Parent parent); Parent getParent(); } // There is no way to avoid this because we would need to define // "Node" recursively. @SuppressWarnings( "rawtypes" ) class Node< Parent extends ParentNode, Child extends ChildNode > implements ParentNode, ChildNode { private Parent parent; public Parent getParent() { return parent; } public void setParent(Parent parent) { this.parent = parent; // Here, we must case the child to a type that will accept Node @SuppressWarnings( "unchecked" ) ParentNode cast = (ParentNode)parent; cast.addChild(this); // Note: Either add the child here ... } private List children; public List getChildren() { return children; } public void addChild( Child child ) { children.add(child); // Here, we must case the child to a type that will accept Node @SuppressWarnings( "unchecked" ) ChildNode cast = (ChildNode)child; cast.setParent(this); // ... or here but not twice :-) } } 

即将两个函数(向上看并向下看)分成两个接口,然后创建一个实现两者的节点类型。 这允许您将叶子和根节点定义为特殊节点(没有两个API中的一个),或者您可以像任何其他节点一样定义它们并在“不支持”方法中返回null

用法:

 public class DirFileNode extends Node { } public class TreeUsage { public static void main( String[] args ) { DirFileNode node = new DirFileNode(); DirFileNode node2 = new DirFileNode(); node.addChild( node2 ); // Why yes, I do love infinite loops. How can you tell? node2.addChild( node ); } } 

您可以看到API确保一切都是类型安全的,但在内部,您必须强制转换。 只要您不在节点中使用generics类型,这只是简单的。 如果你这样做,声明会变得非常混乱。