2012-06-07 2 views
4

У меня есть несколько классов: SearchResponse, SearchResponseHit, SpecialSearchResponse (расширяет SearchResponse) и SpecialSearchResponseHit (расширяет SearchResponseHit).Почему интерфейс java Iterable не может использовать подстановочный шаблон generics? Или: почему я не могу переопределить метод iterator(), чтобы вернуть Iterator для подкласса?

SearchResponse выглядеть примерно так:

public class SearchResponse implements Iterable<SearchResponseHit> { 

    [...] 

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

Это делает возможным для меня, чтобы использовать экземпляр SearchResponse в цикле Еогеасп, как это:

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

Теперь, что я хочу do, но не может выяснить, как сделать этот код компиляцией, когда у меня есть экземпляр 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() 

I попытались изменить метод в 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 (и другие функции, которые используют интерфейс Iterable за кадром), и написать метод getSpecialIterator(), а затем использовать итератор напрямую?

С уважением /J

+0

Я думаю, это проблема генериков, а не Iterable. Кроме того, вы не показали, что 'SearchResponse' расширяет' SearchResponseHit'. – user845279

+0

Ну, независимо от того, как классифицировать эту проблему («generics» или «Iterable»), проблема реальна и ощутима. Кроме того, SearchResponse не поддерживает и не должен расширять SearchResponseHit, поэтому откуда вы это взяли? – user1442411

+0

Mybad, я имел в виду 'SpecialSearchResponse', но я понимаю, что вы имеете в виду. – user845279

ответ

9

Одним из способов было бы объявить различные классы следующим образом:

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 ssr = new SpecialSearchResponse(); 
    for (SpecialSearchResponseHit h : ssr) {} 

Но который вводит дженерики в классе SearchResponse, и вы не можете просто объявить SearchResponse sr = new SearchResponse() (без предупреждений & отливок).


UPDATE
После вашего комментария, вы могли бы в качестве альтернативы создать общий суперкласс, содержащие дженерик шаблонного - вы могли бы сделать его абстрактным и частный пакет, так что пользователь ваших классов не видит:

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 ssr = new SpecialSearchResponse(); 
for (SpecialSearchResponseHit h : ssr) {} 
+0

Спасибо за ваше предложение, assylias. Но я должен сказать, что добавление этого дополнительного кода просто для того, чтобы сделать компилятор счастливым, но кодер недоволен, для меня недостаточно. Я не могу понять, почему Java работает против меня в этом случае. Какова логическая причина этого ограничения? – user1442411

+0

@ user1442411 См. Мой обновленный ответ. – assylias

+0

Спасибо за обновленное предложение, я отметил ваше предложение в качестве принятого ответа (хотя у вас недостаточно репутации, чтобы сделать голосование). Однако я думаю, что на самом деле предпочитаю сохранять простую структуру классов, которые у меня есть сейчас, где SpecialSearchResponse фактически расширяет SearchResponse (с абстрактным предком, который будет хорошо, если мне никогда не придется ссылаться на него извне, но бывают случаи, когда я буду иметь тоже необходимо). Поэтому, я думаю, я просто должен согласиться с тем, что Java ограничена таким образом. – user1442411