Why can't java Iterable interface use generics wildcard? Or: why can't I override the iterator () method to return an Iterator for a subclass?

I have several classes: SearchResponse, SearchResponseHit, SpecialSearchResponse (extends SearchResponse) and SpecialSearchResponseHit (extends SearchResponseHit).

SearchResponse looks something like this:

public class SearchResponse implements Iterable<SearchResponseHit> { [...] public Iterator<SearchResponseHit> iterator() { return searchResponseHits.iterator(); } } 

This allows me to use an instance of SearchResponse in a foreach loop, for example:

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

Now, what I want to do, but cannot figure out how to make this code compile when I have an instance of 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:

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

... I get an error message:

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

I tried changing the method in SearchResponse to:

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

... but this gives me an 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 most beautiful) way to solve this problem? Or do I need to skip the foreach method (and other functions that use the Iterable interface behind the scenes) and write the getSpecialIterator () method and then use the iterator directly?

Relations / J

+4
source share
2 answers

One way is to declare the various classes as follows:

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

So 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 this introduces generics in the SearchResponse class, and you cannot just declare SearchResponse sr = new SearchResponse() anymore (without warnings and casts).


UPDATE
Following your comment, you can also create a general superclass containing the generics template template - you can make it abstract and private package so that 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 up 2 children as you would like:

 SearchResponse sr = new SearchResponse(); for (SearchResponseHit h : sr) {} SpecialSearchResponse ssr = new SpecialSearchResponse(); for (SpecialSearchResponseHit h : ssr) {} 
+9
source

The accepted answer has good workarounds, but, first of all, why is this a problem because Iterable is poorly defined. The iterator method signature must be Iterator<? extends T> iterator() Iterator<? extends T> iterator() Iterator<? extends T> iterator() Iterator<? extends T> iterator() . Since all Iterator<T> methods use a parameter of type T only in a covariant manner, Iterator<T> equivalent to Iterator<? extends T> Iterator<? extends T> Iterator<? extends T> Iterator<? extends T> , but the wildcard version is more flexible. The only argument against this is if you think that in the future you will add a contravariant use of T (e.g. insert(T obj) ). However, in any case, this is likely to require a new interface.

0
source

Source: https://habr.com/ru/post/1416594/


All Articles