I have some classes: SearchResponse, SearchResponseHit, SpecialSearchResponse (extends SearchResponse) and SpecialSearchResponseHit (extends SearchResponseHit).
SearchResponse look something like this:
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:
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:
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 the error:
The return type is incompatible with Iterable<SearchResponseHit>.iterator()
I have tried changing the method in SearchResponse to:
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?
Regards /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).
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:
SearchResponse sr = new SearchResponse();
for (SearchResponseHit h : sr) {}
SpecialSearchResponse ssr = new SpecialSearchResponse();
for (SpecialSearchResponseHit h : ssr) {}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With