正如标题所说,我正在尝试设计一个自定义数据结构SearchTree
,该结构需要Iterable
而不是SearchTree.Entry<K, V>
等条目。 SearchTree
本身只是一个界面。
我想做的是让任何使用它自己的SearchTree<K, V>
和K
子类实现V
的类也能够实现SearchTree<K, V>
中定义的迭代器。 1}} interface。
SearchTree.java
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
个对象,因此将它声明为:
BSTIterator.java
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的类型参数&lt;&gt; 。
是否有任何明智的方法来解决这个问题,以便我可以在我的接口中有一个泛型迭代器,并从实现SearchTree
的类中返回具体的迭代器实现,使得泛型类型也可以被子类化?
答案 0 :(得分:2)
你关闭了。您的迭代器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<>();
}
}
答案 1 :(得分:0)
有几种方法可以解决这个问题。
最直接的方式是Neil describes in his answer。但这会丢弃类型信息:例如,如果您想直接使用BSTIterator
,则需要将元素强制转换回BSTNode
s,如果您想这样使用它们。< / p>
hacky(但实际上是完全安全的)方法是注意Iterator<T>
上没有消费者方法:您只能通过get()
从中获取价值(或检查是否有&#39} ; s另一个元素,或删除以前获得的元素。)
因此,将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>>
:您仍然坚持?
。
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隐藏在实现类中。