【问题标题】:Problem with Generics and sub-typing in my generic Tree implementation我的泛型树实现中的泛型和子类型问题
【发布时间】:2011-03-31 18:34:29
【问题描述】:

考虑这段代码:

public class TreeNode<T extends TreeNode<T, E>, E> {
    protected T parent;
    protected E data;
    protected List<T> children = new ArrayList<T>();

    public TreeNode(T parent, E data) {
        this.parent = parent;
        this.data = data;
    }

    public T getRoot() {
        if (parent == null) {
            return this;       //<---- Problem is here!
        } else {
            return getParent().getRoot();

        }
    }

    public T getParent() {
        if (parent == null) {
           throw new RuntimeException("This already the parent!");
        } else {
            return parent;
        }
    }
}

/*
  incompatible types
    required: T
    found:    TreeNode<T,E>
*/

我该如何解决这个问题并让我的代码正常工作?

【问题讨论】:

  • 是的,它消除了在 getParent().getRoot() 之前转换的需要。我想知道如何删除return (T) this;中的最后一个演员

标签: java generics inheritance tree covariance


【解决方案1】:

您想使用所谓的getThis() trick。像这样声明一个新方法:

/** Subclasses must implement this method as {@code return this;} */
protected abstract T getThis();

然后当您需要使用this 时,只需调用getThis() 即可。附带说明一下,实现此方法会混淆@Michael Williamson 的答案中的BadNode 之类的类,因此首先要编写这样的类变得更加困难(这是一件好事)。

【讨论】:

  • Mhhh,如果子类不必实现任何与数据结构相关的东西,我会更喜欢。这或多或少是我所期望的,在每个类的基础上实现该类型。但我希望能够使用TreeNode 而无需对其进行子类化。
  • 我不确定是否可以在没有子类化的情况下做到这一点。你总是可以只声明一个简单的final class ConcreteTreeNode&lt;E&gt; extends TreeNode&lt;ConcreteTreeNode,E&gt;{}
【解决方案2】:

不保证类型 T 与类本身的类型相同,所以需要在不编译的那一行添加强制转换:

public T getRoot() {
    if (parent == null) {
        return (T)this;
    } else {
        return getParent();
    }
}

举一个会暴露输入错误的简单代码示例:

public class GoodNode extends TreeNode<GoodNode, Integer> {
    public GoodNode(GoodNode parent, Integer data) {
        super(parent, data);
    }
}

public class BadNode extends TreeNode<GoodNode, Integer> {
    public BadNode(GoodNode parent, Integer data) {
        super(parent, data);
    }

    public static void main(String[] args) {
        GoodNode node = new BadNode(null, null).getRoot();
    }
}

运行 BadNode.main 会导致输入错误,因为 BadNode(null, null).getRoot() 返回了一个 BadNode 类的对象(因为它没有父级),但是因为 BadNode 扩展了 TreeNode,所以返回类型getRoot() 是 GoodNode。由于 BadNode 无法转换为 GoodNode,因此存在类转换异常:

Exception in thread "main" java.lang.ClassCastException: BadNode cannot be cast to GoodNode
    at BadNode.main(BadNode.java:7)

【讨论】:

  • 是的,我几秒钟前就这样做了。但我不确定这是否安全。这有可能在运行时失败吗?
【解决方案3】:

也许您想“过于笼统”?

public class TreeNode<E> {
    protected TreeNode<E> parent;
    protected E data;
    protected List<TreeNode<E>> children = new ArrayList<TreeNode<E>>();

    public TreeNode(T parent, E data) {
        this.parent = parent;
        this.data = data;
    }

    public TreeNode<E> getRoot() {
        if (parent == null) {
            return this;       
        } else {
            return getParent(); // <--- ???
        }
    }
...
}

顺便说一句:您可能想要调用类似 parent.getRoot() 而不是 getParent() 的内容。

【讨论】:

  • 问题是我希望TreeNode 的子类型有效。这就是第二个泛型类型存在的原因。
  • 你想要的是子类型整个树,而不仅仅是一些节点?那么@matt-mchenry 的答案中关于 getThis() 技巧的页面很可能就是您要查找的内容。
【解决方案4】:

为什么在 extends 子句中使用原始类型?这可能会阻碍类型推断。 请尝试以下操作:

public class TreeNode<T extends TreeNode<T,E>, E> {

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多