【问题标题】:Generic iterator for an interface and its subclasses and its types and subtypes接口及其子类及其类型和子类型的通用迭代器
【发布时间】:2017-10-18 10:27:09
【问题描述】:

正如标题所说,我正在尝试设计一个自定义数据结构SearchTree,它需要在SearchTree.Entry<K, V> 等条目之上为IterableSearchTree 本身只是一个接口。

我想要做的是让任何实现SearchTree<K, V> 及其自己的子类型KV 的类也能够实现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 的类中返回具体的迭代器实现,这样泛型类型也可以被子类化?

【问题讨论】:

  • Comparable&lt;? super K&gt; 中删除? super。将对象与其超类型的实例进行比较是没有意义的。

标签: java generics polymorphism


【解决方案1】:

你已经接近了。你的迭代器BSTIterator 必须实现Iterator&lt;SearchTree.Entry&lt;K, V&gt;&gt; 而不是Iterator&lt;BSTNode&lt;K, V&gt;&gt;。我认为这是因为您返回的迭代器的类型必须保证它是任何Entry&lt;K, V&gt;,而不是特别是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<>();
    }
}

【讨论】:

    【解决方案2】:

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


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


    hacky(但实际上完全安全)的方法是注意 Iterator&lt;T&gt; 上没有消费者方法:您只能通过 get() 从中获取值(或检查是否有另一个元素,或删除以前得到的元素)。

    因此,将Iterator&lt;SubclassOfT&gt; 用作Iterator&lt;T&gt; 是完全安全的。 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&lt;? extends SearchTree.Entry&lt;K, V&gt;&gt;:你被那个? 困住了。

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

    【讨论】:

      猜你喜欢
      • 2014-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-24
      相关资源
      最近更新 更多