接口及其子类及其类型和子类型的通用迭代器

时间:2017-10-18 10:27:09

标签: java generics polymorphism

正如标题所说,我正在尝试设计一个自定义数据结构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的类中返回具体的迭代器实现,使得泛型类型也可以被子类化?

2 个答案:

答案 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隐藏在实现类中。