Не используйте дженерики для неоднородных деревьев, используйте интерфейсы и приведение.
Несмотря на то, что вы можете решить некоторые проблемы с помощью обобщений, полученный код будет хрупким, покажет вам сообщения об ошибках, которых вы никогда не видели, исправление ошибок обычно приводит к 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 :-)
}
}
т.е.. разделите две функции (посмотрите вверх и посмотрите вниз) на два интерфейса, а затем создайте тип узла, который реализует оба. Это позволяет вам определять листья и корневые узлы как специальные узлы (без одного из двух 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 гарантирует, что все безопасно для типов, но внутри вы должны выполнить приведение. И это просто, если вы не используете универсальные типы в узлах. Если вы это сделаете, объявления, которые превращаются в огромный беспорядок.