- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我要为我的一个项目添加通用性。我喜欢泛型,因为它使我的代码更健壮、 self 记录并消除了所有那些丑陋的转换。
但是,我遇到了一个棘手的案例,并且在尝试为我的一个结构表达“递归”约束时遇到了一些问题。
这基本上是某种“通用”树,带有双链接(到子节点和父节点)。我最大限度地简化了类(class)以显示问题:
public class GenericTree<
ParentClass extends GenericTree<?, ?>,
ChildClass extends GenericTree<?, ?>>
{
// Attributes
private ArrayList<ChildClass> children = new ArrayList<ChildClass>();
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 给出以下错误:
Bound mismatch: The method setParent(?) of type ChildClass is not applicable for the
arguments (GenericTree). The wildcard parameter ? has no lower bound, and may actually be more restrictive than argument GenericTree
我想要的是能够表达类似的东西:
public class GenericTree<
ParentClass extends GenericTree<?, ?>,
ChildClass extends GenericTree<[THIS_VERY_CLASS], ?>>
要说子类的父类应该是自己...
我看过一些关于自绑定(bind)泛型的文章,但我不知道如何在这种情况下应用它。
如有任何帮助,我们将不胜感激。
最佳答案
不要对非均匀树使用泛型,使用接口(interface)和强制转换。
虽然您可以使用泛型解决一些问题,但生成的代码会很脆弱,向您显示您以前从未见过的错误消息,修复错误通常会导致 try&error,即使您可以编译它,您不知道为什么 ;-)
[EDIT2] 添加了 addChild()
和下面的用法示例。
[编辑] 还在我身边吗?如果确实需要,请使用此 API:
interface ParentNode<Child> {
List<Child> getChildren();
void addChild(Child child);
}
interface ChildNode<Parent> {
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<? extends Node>,
Child extends ChildNode<? extends Node>
>
implements
ParentNode<Child>,
ChildNode<Parent>
{
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<Node> cast = (ParentNode)parent;
cast.addChild(this); // Note: Either add the child here ...
}
private List<Child> children;
public List<Child> 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<Node> cast = (ChildNode)child;
cast.setParent(this); // ... or here but not twice :-)
}
}
即将这两个功能(向上看和向下看)拆分为两个接口(interface),然后创建一个实现这两个接口(interface)的节点类型。这允许您将叶节点和根节点定义为特殊节点(没有两个 API 之一),或者您可以像定义任何其他节点一样定义它们并在“不受支持”的方法中返回 null
。
用法:
public class DirFileNode extends Node<DirFileNode, DirFileNode> {
}
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 确保所有内容都是类型安全的,但在内部,您必须强制转换。只要您不在节点中使用泛型类型,这就很简单。如果这样做,声明会变得一团糟。
关于java - 泛型树,自界泛型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3333590/
我是一名优秀的程序员,十分优秀!