从通用树到特定领域的树
From a Generic Tree To a Domain Specific Tree
我想使用由领域特定节点 DomainNode
组成的领域特定树 DomainTree
,但在模板 类 Tree
和 [=15] 中保留所有通用函数=].首先,我从模板 Tree<T>
和 Node<T>
开始(其中 T 是节点数据的类型)。 DomainTree
然后与 Node<T>
界面一起工作,这不是我想要的。它应该适用于 DomainNode
个对象。
为了解决这个问题,我将通用树的模板参数更改为 Tree<N extends Node<?>>
(下面的实现)。现在我可以通过将树实例化为 DomainTree<DomainNode>
.
来使用 DomainNode
仍然,我在 (1) 处遇到编译错误,因为 getChildren()
returns Node<T>
的列表似乎无法转换为 [= 的列表26=],尽管我确保 N extends Node<?>
.
为什么这不起作用,我该如何设计它,以便 DomainTree
可以与 DomainNode
一起使用?
通用树
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> {
}
如果您不是很喜欢泛型,它会给您带来一些惊喜。首先请记住,存在类型擦除,因此编译器和运行时会看到不同的东西。粗略地说,这也限制了编译器分析源代码的能力。
请注意 List<Node<N>>
和 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(或更新版本)或类似的东西,这对泛型非常有用。
从你的代码中我得到的印象是你混合了不同的抽象层,因为 Node<T>
class 中有 T data
并且在 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> {
}
目前代码的问题在于,对于给定的 Node<T>
,编译器无法知道从 toList()
返回的 List
的类型是与 class 本身相同 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...
}
}
}
我还增加了一些其他效率。
我想使用由领域特定节点 DomainNode
组成的领域特定树 DomainTree
,但在模板 类 Tree
和 [=15] 中保留所有通用函数=].首先,我从模板 Tree<T>
和 Node<T>
开始(其中 T 是节点数据的类型)。 DomainTree
然后与 Node<T>
界面一起工作,这不是我想要的。它应该适用于 DomainNode
个对象。
为了解决这个问题,我将通用树的模板参数更改为 Tree<N extends Node<?>>
(下面的实现)。现在我可以通过将树实例化为 DomainTree<DomainNode>
.
DomainNode
仍然,我在 (1) 处遇到编译错误,因为 getChildren()
returns Node<T>
的列表似乎无法转换为 [= 的列表26=],尽管我确保 N extends Node<?>
.
为什么这不起作用,我该如何设计它,以便 DomainTree
可以与 DomainNode
一起使用?
通用树
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> {
}
如果您不是很喜欢泛型,它会给您带来一些惊喜。首先请记住,存在类型擦除,因此编译器和运行时会看到不同的东西。粗略地说,这也限制了编译器分析源代码的能力。
请注意 List<Node<N>>
和 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(或更新版本)或类似的东西,这对泛型非常有用。
从你的代码中我得到的印象是你混合了不同的抽象层,因为 Node<T>
class 中有 T data
并且在 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> {
}
目前代码的问题在于,对于给定的 Node<T>
,编译器无法知道从 toList()
返回的 List
的类型是与 class 本身相同 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...
}
}
}
我还增加了一些其他效率。