简体   繁体   English

为什么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?

I have some classes: SearchResponse, SearchResponseHit, SpecialSearchResponse (extends SearchResponse) and SpecialSearchResponseHit (extends SearchResponseHit). 我有一些类:SearchResponse,SearchResponseHit,SpecialSearchResponse(扩展SearchResponse)和SpecialSearchResponseHit(扩展SearchResponseHit)。

SearchResponse look something like this: SearchResponse看起来像这样:

public class SearchResponse implements Iterable<SearchResponseHit> {

    [...]

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

This makes it possible for me to use an instance of SearchResponse in a foreach loop, like this: 这使我可以在foreach循环中使用SearchResponse的实例,如下所示:

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

Now, what I want to do, but can't find out how, is to make this code compile when I have an instance of SpecialSearchResponse: 现在,当我有一个SpecialSearchResponse实例时,我想做的,但无法弄清楚如何编译这段代码:

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

This gives me the following compiler error: 这给了我以下编译器错误:

Type mismatch: cannot convert from element type SearchResponseHit to SpecialSearchResponseHit

If I try to add this code to SpecialSearchResponse: 如果我尝试将此代码添加到SpecialSearchResponse:

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

...I get the error: ......我收到错误:

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

I have tried changing the method in SearchResponse to: 我尝试将SearchResponse中的方法更改为:

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

...but this gives me the error: ......但这给了我错误:

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

Then I tried changing the class definition to: 然后我尝试将类定义更改为:

public class SearchResponse implements Iterable<? extends SearchResponseHit>

...but this gives me this error: ...但这给了我这个错误:

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

What is the best (and prettiest) way to solve this? 解决这个问题的最好(也是最漂亮)的方法是什么? Or do I have to skip the foreach-method (and other functions that use the Iterable interface behind the scenes) and write a getSpecialIterator() method and then use the iterator directly? 或者我是否必须跳过foreach方法(以及在幕后使用Iterable接口的其他函数)并编写getSpecialIterator()方法然后直接使用迭代器?

Regards /J 问候/ J.

One way would be to declare the various classes in the following way: 一种方法是以下列方式声明各种类:

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 {}

That way you can call them like this: 这样你可以像这样调用它们:

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

    SpecialSearchResponse ssr = new SpecialSearchResponse();
    for (SpecialSearchResponseHit h : ssr) {}

But that introduces generics in the SearchResponse class and you can't simply declare SearchResponse sr = new SearchResponse() any longer (without warnings & casts). 但是这会在SearchResponse类中引入泛型,你不能再简单地声明SearchResponse sr = new SearchResponse() (没有警告和强制转换)。


UPDATE UPDATE
Following your comment, you could alternatively create a common superclass that contains the generics boilerplate - you could make it abstract and package private so the user of your classes does not see it: 在您的评论之后,您可以创建一个包含泛型样板的公共超类 - 您可以将其设为抽象并打包私有,以便您的类的用户看不到它:

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> {}

Now you can call the 2 children as you wanted: 现在您可以根据需要调用2个孩子:

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

SpecialSearchResponse ssr = new SpecialSearchResponse();
for (SpecialSearchResponseHit h : ssr) {}

The accepted answer has good work-arounds, but as to why this is a problem in the first place, it's because Iterable is poorly defined. 接受的答案有很好的解决方法,但至于为什么这首先是一个问题,这是因为Iterable定义不明确。 The signature of the iterator method should be Iterator<? extends T> iterator() 迭代器方法的签名应该是Iterator<? extends T> iterator() Iterator<? extends T> iterator() . Iterator<? extends T> iterator() Since all of Iterator<T> 's methods only use the type parameter T in a covariant way, Iterator<T> is equivalent to Iterator<? extends T> 由于Iterator<T>的所有方法只以协变方式使用类型参数T,因此Iterator<T>等同于Iterator<? extends T> Iterator<? extends T> , but the wildcard version is more flexible. Iterator<? extends T> ,但通配符版本更灵活。 The only argument against doing this is if you think you will add contravariant usages of T in the future (like insert(T obj) for example). 反对这样做的唯一理由是,如果您认为将来会添加T的逆变使用(例如insert(T obj) )。 However, doing this would probably warrant a new interface anyway. 但是,这样做可能会保证一个新的界面。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM