如何在Java中处理自引用和继承

时间:2014-09-12 19:09:19

标签: java generics inheritance

我希望字段“children”包含具有包含对象类型的对象列表。但是,当继承时,由于向下转换,我会收到错误。我可以使用哪些策略来保持层次结构,但能够适当地访问子对象?

public class Node<T>{

    protected T value;
    protected Node<T> parent;
    private ArrayList<Node<T>> children;

    public Node(T value){
        this.value = value;
        this.children = new ArrayList<Node<T>>();
    }

    public ArrayList<Node<T>> getChildren(){
        return this.children;
    }

    //...other methods...//

}

当我尝试在此类中调用getChildren()时,出现“类型不匹配”错误,因为它正在尝试向下转换。

public class DecidableTree<T extends Decidable<T>> extends Node<T>{

    public DecidableTree(T value) {
        super(value);
    }

    public randomInvolvedFunction(){
        //...other code...//
        for(DecidableTree<T> child : this.getChildren()){
            child.decidableTreeSpecificMethod();
        }
        //...other code...//
    }

    //...other methods...//

}

不幸的是,我不能只重写getChildren()函数,因为返回类型必须匹配。

1 个答案:

答案 0 :(得分:11)

问题

您遇到的问题是您确实在倾向,您试图将Node的任何给定实例视为DecidableTree。虽然您确实可以将DecidableTree的任何实例视为Node,因为它继承自Node,但这种方法并不相反。

解决方案

坏道路

您当然可以使用instanceOf运算符直接检查实例的类型是否正确,但有一种更简洁的方法可以执行此操作。

清洁方式

您可以通过将Node类参数化为两个通用值来实现此目的。一个V用于保存在给定Node的值,另一个T表示Node的实际具体实例。

例如,

public abstract class Node<T, V> {
    protected V value;
    protected Node<T,V> parent;
    private List<T> children;

    public Node(V value){
        this.value = value;
        this.children = new ArrayList<T>();
    }

    public List<T> getChildren(){
        return this.children;
    }

    public void addChild(T child){
        this.children.add(child);
    }

    public V getVal(){
        return this.value;
    }
}

打破这个

通过额外的类型变量NodeT进行参数化,这允许我们在父类中返回给定具体类型的值,而不会实际知道具体类型是什么。如果这仍然令人困惑,考虑到DecidableTree的新实例可能会帮助您了解正在发生的事情,

public class DecidableTree<V> extends Node<DecidableTree<V>, V> {

    public DecidableTree(V value) {
        super(value);
    }

    public void randomInvolvedFunction(){
        for(DecidableTree<V> child : this.getChildren()){
            System.out.println(child.getVal());
        }
    }
}
对于实际值类型,

DecidableTree仍然是通用的,但对T而言,它不是通用的。它说T是它自己的一个实例。这允许我们从父节点获取值而不需要向下转换。

这将编译并正常工作,现在您可以根据泛型直接描述所有类型。注意我添加了一些方法,以便您可以进行一些有意义的测试。

最后一个注释

在您的示例中,除了实例化的行之外,我还将Node更改为抽象,将ArrayList更改为List。我假设您永远不会直接创建Node的实例,因此它应该是abstract。就ArrayList而言,最佳做法是通过接口引用您的数据结构,在本例中为List,而不是通过实际实现(除非有一些非常具体的原因) 。这样,您只需更改一行代码即可轻松更改 very 的数据结构。