1

我想使用DomainTree由域特定节点组成的域特定树DomainNode,但将所有通用函数保留在模板类TreeNode. 首先,我从模板Tree<T>Node<T>(其中 T 是节点数据的类型)开始。当时DomainTree正在使用Node<T>界面,这不是我想要的。它应该适用于DomainNode对象。

为了解决这个问题,我将通用树的模板参数更改为Tree<N extends Node<?>>(下面的实现)。现在我可以DomainNode通过将树实例化为DomainTree<DomainNode>.

不过,我在 (1) 处收到编译错误,因为getChildren()返回了Node<T>的列表N,尽管我确定了N extends Node<?>.

为什么这不起作用,我该如何设计它,以便DomainTree可以与DomainNodes 一起使用?

通用树

import java.util.ArrayList;
import java.util.List;

class Tree<N extends Node<?>> {

    public N rootElement;

    public List<N> toList() {
        List<N> list = new ArrayList<N>();
        walk(rootElement, list);
        return list;
    }  

    private void walk(N element, List<N> list) {
        list.add(element);
        List<N> children = element.getChildren(); // (1) Cannot convert from List<Node<T>> to List<T>
        for (N data : children) {
            walk(data, list);
        }
    }
}

class Node<T> {

    public T data;
    public List<Node<T>> children;

    public List<Node<T>> getChildren() {
        if (this.children == null) {
            return new ArrayList<Node<T>>();
        }
        return this.children;
    }

    public void addChild(Node<T> child) {
        if (children == null) {
            children = new ArrayList<Node<T>>();
        }
        children.add(child);
    }
}

问题特定树

class DomainTree extends Tree<DomainNode> {

    public void build() {
            for (DomainNode node : toList()) {
                // process...
            }
        }
}

class DomainNode extends Node<String> {

}
4

2 回答 2

0

代码的问题在于,对于给定Node<T>的 ,编译器无法知道List返回的类型与类本身toList()相同 Node<T>

你需要的是一个自引用的泛型类型:

class Node<T, N extends Node<T, N>> {

    public T data;
    public List<N> children;

    public List<N> getChildren() {
        return children == null ? Collections.<N>emptyList() : children;
    }

    public void addChild(N child) {
        if (children == null) {
            children = new ArrayList<N>();
        }
        children.add(child);
    }
}

现在返回toList()的类型与类型本身的类型相同。

然后DomainNode变成:

class DomainNode extends Node<String, DomainNode> {
    //
}

并且签名的Tree变化稍微变成:

class Tree<N extends Node<?, N>> {

您的使用示例现在可以编译:

class DomainTree extends Tree<DomainNode> {
    public void build() {
        for (DomainNode node : toList()) {
            // process...
        }
    }
}

我还添加了其他一些效率。

于 2015-02-09T14:17:44.113 回答
0

如果你不是很喜欢,泛型会带来一些惊喜。首先请记住,存在类型擦除,因此编译器和运行时会看到不同的东西。粗略地说,这也限制了编译器分析源代码的能力。

List<Node<N>>请注意, a和 a之间确实存在差异List<N>。因此,即使N extends Node<?>分配了'List children = element.getChildren();' 天生就坏了。

此外,随着您的声明Tree<N extends Node<?>>,您会期望您可以编写类似List<Node<?>> l2 = element.getChildren();. 不幸的是,由于泛型的某些子类,这不起作用。例如,如果您将代码更改为class Tree<N extends Node<N>>(这可能不是您想要的),您可以编写List<Node<N>> l2 = element.getChildren();.

我建议学习 Sun Certified Java Programmer Study Guide for Java 6(或更新版本)或类似的东西,这对泛型很有帮助。

从您的代码中,我得到的印象是您混合了不同的抽象层,因为T dataNode<T>类中有 并且在 for each 循环中调用了元素N data。但是,在循环中,您有一个N extends Node<?>T data. 因此,您的代码的意图对我来说仍然有点不清楚。如果您的代码为固定版本,则为工作草案(Eclipse Luna,JDK 6)

package generics.tree;

import java.util.ArrayList;
import java.util.List;

class Tree<T> {

    public Node<T> rootElement;

    public List<Node<T>> toList() {
        List<Node<T>> list = new ArrayList<Node<T>>();
        walk(rootElement, list);
        return list;
    }  

    private void walk(Node<T> element, List<Node<T>> list) {
        list.add(element);
        List<Node<T>> children = element.getChildren(); // (1) Cannot convert from List<Node<T>> to List<T>
        for (Node<T> data : children) {
            walk(data, list);
        }
    }
}

class Node<T> {

    public T data;
    public List<Node<T>> children;

    public List<Node<T>> getChildren() {
       if (this.children == null) {
           return new ArrayList<Node<T>>();
       }
       return this.children;
    }

    public void addChild(Node<T> child) {
        if (children == null) {
           children = new ArrayList<Node<T>>();
        }
        children.add(child);
    }
}

class DomainTree extends Tree<String> {

    public void build() {
        for (Node<String> node : toList()) { // changed!
            // process...
        }
    }
}

class DomainNode extends Node<String> {
}
于 2015-02-09T12:36:20.810 回答