Java, итерируемый<Iterable> в ArrayList<ArrayList>

#java #generics #iterator

#java #дженерики #итератор

Вопрос:

Возможно, я что-то упускаю, но я подумал, что если я объявлю свой класс как таковой:

 public class Something<T> implements Iterable<Iterable<T>> {
  public Something(Iterable<Iterable<T>> input) {
  ...
  

Я должен быть в состоянии создать его экземпляр как таковой:

 ArrayList<ArrayList<String>> l = new ArrayList<ArrayList<String>>();

Something<String> s = Something<String>(l);
  

К сожалению, это выдает мне ошибку. Я думал, что ArrayLists являются итерируемыми, поэтому они должны точно соответствовать определению моего конструктора.

Ответ №1:

Вам нужно изменить свой конструктор, чтобы принимать все, что расширяет Iterable<T>

 public Something(Iterable<? extends Iterable<T>> list){}
  

Причина этого в том, что универсальные типы не могут буквально расширять другие универсальные типы. A List<String> не может быть присвоен a List<CharSequence> . Ключевое слово extends допускает этот тип присваивания.

Комментарии:

1. Спасибо, но теперь я получаю предупреждения здесь: public class Something<T> implements Iterable<Iterable<T>> { private Iterable<Iterable<T>> input; public Something(Iterable<? extends Iterable<T>> input) { this.input = (Iterable<Iterable<T>>)input; }

2. Я предлагаю найти копию Effective Java Джоша Блоха и прочитать «PECS». Это поможет вам действительно понять, что происходит. В любом случае вам нужно иметь Iterable<? extends Iterable<T> везде, где вы собираетесь назначить ArrayList<ArrayList<String>>

3. или, возможно, даже Iterable<? extends Iterable<? extends T>>

Ответ №2:

Подтипирование с помощью generics очень неинтуитивно. Для того, что вы хотите сделать, вам нужны подстановочные знаки, и это было бы довольно некрасиво.

В разделе 3 есть отличное объяснение — http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf