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

标签 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() 函数,因为返回类型必须匹配。

最佳答案

问题

您遇到的问题是您确实在向下转型,您试图将 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;
    }
}

拆解

通过用额外的类型变量T参数化Node,这允许我们返回父类中给定具体类型的值,而无需真正知道那是什么具体类型将是。如果这仍然令人困惑,考虑我们的新实例 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,最佳做法是通过接口(interface)引用数据结构,在本例中为 List,而不是通过实际实现(除非有一些非常具体的原因不这样做)。这使您将来可以非常轻松地更改数据结构,只需更改一行代码即可。

关于java - Java中如何处理自引用和继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25815183/

相关文章:

Java 防止在类外调用私有(private)或 protected 方法

java - 尝试隐藏空值以防止打印

java - 如何让泛型函数根据Java中的输入获取对象属性

c# - 伪多重继承

java - 我们可以在java中将任何字符串日期值转换为日期yyyy-MM-dd吗?

java - 具有可配置凭据的 Spring WebServiceClient

generics - 为什么不能在Rc <dyn Fn()>内使用此闭包?

c# - 在运行时指定通用委托(delegate)类型参数

c++ - 如何/何时使用虚拟析构函数?

c# - 处理继承类中参数的最佳方法