java Iterable接口为啥不能带泛型通配符?或者:为啥我不能重写 iterator() 方法为子类返回一个 Iterator?

Posted

技术标签:

【中文标题】java Iterable接口为啥不能带泛型通配符?或者:为啥我不能重写 iterator() 方法为子类返回一个 Iterator?【英文标题】:Why can't the java Iterable interface take a generics wildcard? Or: why can't I an overriding iterator() method return an Iterator for a subclass?java Iterable接口为什么不能带泛型通配符?或者:为什么我不能重写 iterator() 方法为子类返回一个 Iterator? 【发布时间】:2012-06-11 15:25:33 【问题描述】:

我有一些类:SearchResponse、SearchResponseHit、SpecialSearchResponse(扩展 SearchResponse)和 SpecialSearchResponseHit(扩展 SearchResponseHit)。

SearchResponse 看起来像这样:

public class SearchResponse implements Iterable<SearchResponseHit> 

    [...]

    public Iterator<SearchResponseHit> iterator() 
        return searchResponseHits.iterator();
    

这使我可以在 foreach 循环中使用 SearchResponse 的实例,如下所示:

for (SearchResponseHit hit : mySearchResponse) 
    [...]

现在,我想做但不知道怎么做,就是在我有一个 SpecialSearchResponse 实例时编译这段代码:

for (SpecialSearchResponseHit specialHit : mySpecialSearchResponse) 
    [...]

这给了我以下编译器错误:

Type mismatch: cannot convert from element type SearchResponseHit to SpecialSearchResponseHit

如果我尝试将此代码添加到 SpecialSearchResponse:

public Iterator<SpecialSearchResponseHit> iterator() 
    [...]

...我收到错误:

The return type is incompatible with Iterable<SearchResponseHit>.iterator()

我已尝试将 SearchResponse 中的方法更改为:

    public Iterator<? extends SearchResponseHit> iterator() 
        return searchResponseHits.iterator();
    

...但这给了我错误:

The return type is incompatible with Iterable<SearchResponseHit>.iterator()

然后我尝试将类定义更改为:

public class SearchResponse implements Iterable<? extends SearchResponseHit>

...但这给了我这个错误:

    The type SearchResponse cannot extend or implement Iterable<? extends SearchResponseHit>. A supertype may not specify any wildcard

解决这个问题最好(也是最漂亮)的方法是什么?还是我必须跳过foreach-method(以及其他在后台使用Iterable接口的函数)并编写一个getSpecialIterator()方法,然后直接使用迭代器?

问候 /J

【问题讨论】:

我认为这是一个泛型问题,而不是 Iterable。此外,您还没有显示SearchResponse extends SearchResponseHit 好吧,不管如何对这个问题进行分类(“泛型”或“Iterable”),问题都是真实而明显的。此外,SearchResponse 不会也不应该扩展 SearchResponseHit,那么您是从哪里得到的呢? 我的错,我的意思是 SpecialSearchResponse 但我明白你的意思。 【参考方案1】:

一种方法是通过以下方式声明各种类:

public class SearchResponse<T  extends SearchResponseHit> implements Iterable<T> 
    List<T> searchResponseHits;

    public Iterator<T> iterator() 
        return searchResponseHits.iterator();
    


public class SearchResponseHit 

public class SpecialSearchResponse extends SearchResponse<SpecialSearchResponseHit> 
public class SpecialSearchResponseHit extends SearchResponseHit 

这样你就可以这样称呼他们:

    SearchResponse<SearchResponseHit> sr = new SearchResponse<SearchResponseHit>();
    for (SearchResponseHit h : sr) 

    SpecialSearchResponse s-s-r = new SpecialSearchResponse();
    for (SpecialSearchResponseHit h : s-s-r) 

但这在SearchResponse 类中引入了泛型,您不能再简单地声明SearchResponse sr = new SearchResponse()(没有警告和强制转换)。


更新 根据您的评论,您也可以创建一个包含泛型样板的通用超类 - 您可以将其设为抽象并封装私有,以便您的类的用户看不到它:

abstract class AbstractSearchResponse<T  extends SearchResponseHit> implements Iterable<T>
    List<T> searchResponseHits;

    public Iterator<T> iterator() 
        return searchResponseHits.iterator();
    


public class SearchResponse extends AbstractSearchResponse<SearchResponseHit>  
public class SpecialSearchResponse extends AbstractSearchResponse<SpecialSearchResponseHit> 

现在您可以随意调用 2 个孩子了:

SearchResponse sr = new SearchResponse();
for (SearchResponseHit h : sr) 

SpecialSearchResponse s-s-r = new SpecialSearchResponse();
for (SpecialSearchResponseHit h : s-s-r) 

【讨论】:

感谢您的建议,阿西利亚斯。但我不得不说,添加这些额外的代码只是为了让编译器高兴,但编码器不高兴,对我来说还不够好。我真的不明白为什么 Java 在这种情况下对我不利。这种限制背后的逻辑原因是什么? 感谢您更新的建议,我将您的建议标记为已接受的答案(但没有足够的声誉来投赞成票)。但是我认为实际上更喜欢保留我现在拥有的简单类结构,SpecialSearchResponse 实际上扩展了 SearchResponse (如果我永远不必从外部引用它,那么有一个抽象的祖先就可以了,但是在某些情况下我会也需要)。所以我想我只能接受 Java 在这种方式上受到限制的事实。【参考方案2】:

接受的答案有很好的解决方法,但至于为什么这是一个问题,这是因为 Iterable 的定义不明确。迭代器方法的签名应该是Iterator&lt;? extends T&gt; iterator()。由于Iterator&lt;T&gt;的所有方法仅以协变方式使用类型参数T,所以Iterator&lt;T&gt;等价于Iterator&lt;? extends T&gt;,但通配符版本更灵活。反对这样做的唯一理由是,如果您认为将来会添加 T 的逆变用法(例如 insert(T obj))。但是,无论如何,这样做可能需要一个新界面。

【讨论】:

以上是关于java Iterable接口为啥不能带泛型通配符?或者:为啥我不能重写 iterator() 方法为子类返回一个 Iterator?的主要内容,如果未能解决你的问题,请参考以下文章

java instanceof list带泛型

java教程——泛型

java子类重写父类带泛型的方法

Java 之 泛型擦除

java泛型---通配符,泛型嵌套

java学习----泛型