[英]Generic iterator for an interface and its subclasses and its types and subtypes
正如标题所说,我试图设计一个自定义的数据结构SearchTree
这将需要Iterable
通过条目,如SearchTree.Entry<K, V>
SearchTree
本身只是一个接口。
我想要做的是让任何实现SearchTree<K, V>
和它自己的K
和V
子类型的类也能够实现SearchTree<K, V>
接口中定义的迭代器。
搜索树
public interface SearchTree<K extends Comparable<? super K>, V> extends Iterable<SearchTree.Entry<K, V>> {
static interface Entry<K, V> {
K getKey();
V getValue();
void setValue(V value);
}
}
现在假设我有一个实现接口的类。
BST.java
public class BST<K extends Comparable<? super K>, V> implements SearchTree<K, V> {
@Override
public Iterator<Entry<K, V>> iterator() {
// return some bst specific iterator
}
}
BSTNode.java
public class BSTNode<K extends Comparable<? super K>, V> implements SearchTree.Entry<K, V> { // ...
}
现在,很明显, BST Iterator
应该遍历BSTNode
对象,因此将其声明为如下内容是有意义的:
BST迭代器
public class BSTIterator<K extends Comparable<? super K>, V> implements Iterator<BSTNode<K, V>> {
}
但现在回从问题BST.java
其中的实例BSTIterator
应返回,就像这样:
BST.java
public class BST<K extends Comparable<? super K>, V> implements SearchTree<K, V> {
@Override
public Iterator<Entry<K, V>> iterator() {
return new BSTIterator<>();
}
}
现在这不起作用:无法推断 BSTIterator<> 的类型参数。 有没有什么合理的方法可以解决这个问题,这样我就可以在我的接口中有一个通用迭代器,并从实现SearchTree
的类中返回具体的迭代器实现,这样泛型类型也可以被子类化?
你很接近。 您的迭代器BSTIterator
必须实现Iterator<SearchTree.Entry<K, V>>
而不是Iterator<BSTNode<K, V>>
。 我认为这是因为您返回的迭代器的类型必须保证它是任何Entry<K, V>
,而不是专门的BSTNode
。
public class BSTIterator<K extends Comparable<? super K>, V>
implements Iterator<SearchTree.Entry<K, V>> {
// ...
}
public class BST<K extends Comparable<? super K>, V>
implements SearchTree<K, V> {
@Override
public Iterator<SearchTree.Entry<K, V>> iterator() {
return new BSTIterator<>();
}
}
有几种方法可以解决这个问题。
最直接的方式是尼尔在他的回答中所描述的。 但这会丢弃类型信息:例如,如果您想直接使用BSTIterator
,如果您想这样使用它们,则需要将元素转换回BSTNode
s。
hacky(但实际上完全安全)的方法是注意Iterator<T>
上没有使用者方法:您只能通过get()
从中获取值(或检查是否有另一个元素,或删除以前的-得到元素)。
因此,将Iterator<SubclassOfT>
用作Iterator<T>
是完全安全的。 Java 的类型系统不知道它是安全的,因此您必须强制转换:
return (Iterator<SearchTree.Entry<K, V>>) (Iterator<?>) new BSTIterator<K, V>();
并添加@SuppressWarnings("unchecked")
。
这样做的粗略方法是使SearchTree
实现可迭代的上限类型:
public interface SearchTree<K extends Comparable<K>, V>
extends Iterable<? extends SearchTree.Entry<K, V>>
这有点恶心,因为现在iterator()
方法将返回一个Iterator<? extends SearchTree.Entry<K, V>>
Iterator<? extends SearchTree.Entry<K, V>>
:你被困住了?
.
Josh Bloch 在Effective Java 2nd Ed 中声称,如果您的 API 用户不得不关心通配符,那么您的设计就错了。 (第 28 项,特别是第 137 页,如果您手头有副本)。
另一种方法是为Iterator
中的元素类型显式添加类型变量:
public interface SearchTree<K extends Comparable<K>, V, T extends SearchTree.Entry<K, V>>
extends Iterable<T>
现在您可以将您的BST
声明为:
public class BST<K extends Comparable<K>, V>
implements SearchTree<K, V, BSTNode<K, V>>
这传达了最多的类型信息,但我不会声称这绝对更好,因为每当你声明一个包含SearchTree
变量时有 3 个类型变量只是一般的混乱,特别是因为第三个通常会再次包含第一个和第二个,例如
SearchTree<String, Integer, ? extends SearchTree.Entry<String, Integer>>
这里没有超级干净的选择。 就个人而言,我会选择第一个,hacky 选项,因为 a) 它真的很安全! b) hack 隐藏在实现类中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.