2017-10-18 126 views
2

正如标题所示,我试图设计一个自定义数据结构SearchTree,对于SearchTree.Entry<K, V>等条目,该数据结构需要为Iterable。 A SearchTree本身只是一个界面。接口及其子类及其类型和子类型的泛型迭代器

我想要做的是有一个与它自己的KV亚型实现SearchTree<K, V>任何类也能够实现迭代器作为一个SearchTree<K, V>接口中定义。

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 <>的类型参数。 是否有任何明智的方法来解决此问题,以便我可以在我的接口中使用泛型迭代器,并且以类似方式实现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<>(); 
    } 
} 
0

有几种方法可以解决这个问题。


最直接的方法是什么Neil describes in his answer。但是这会丢弃类型信息:例如,如果您想直接使用BSTIterator,则需要将元素转换回BSTNode,如果您想要这样使用它们。


的哈克(但实际上是完全安全)的方法是注意有上Iterator<T>没有消费者的方法:你永远只能得到通过get()从它的值(或者检查是否有其他元素或删除以前得到的元素)。

因此,使用Iterator<SubclassOfT>作为Iterator<T>是完全安全的。 Java的类型系统不知道它是安全的,所以你要投:

return (Iterator<SearchTree.Entry<K, V>>) (Iterator<?>) new BSTIterator<K, V>(); 

,并添加@SuppressWarnings("unchecked")


毛-IN-A-不同路的方式来做到这一点是让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在有效Java第二版声称,如果您的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>> 

这表达了最多的类型信息,但我不会声称这绝对更好,因为每当你声明一个变量持有一个时就有3个类型变量只是通用的杂波,尤其是第三个通常会包含第一个和第二个,例如,

SearchTree<String, Integer, ? extends SearchTree.Entry<String, Integer>> 

有没有超级大清洁选项在这里。就我个人而言,我会选择第一个hacky选项,因为a)它确实很安全! b)黑客隐藏在实施课堂内部。