Как можно добавить элементы в универсальную коллекцию подстановочных знаков?
Почему я получаю ошибки компилятора с этим кодом Java?
1 public List<? extends Foo> getFoos()
2 {
3 List<? extends Foo> foos = new ArrayList<? extends Foo>();
4 foos.add(new SubFoo());
5 return foos;
6 }
Где SubFoo - это конкретный класс, который реализует Foo, а Foo - это интерфейс.
Ошибки, которые я получаю с этим кодом:
- В строке 3: "Не удается создать экземпляр ArrayList Extends Foo>"
- В строке 4: "Метод add(capture#1-of? Extends Foo) в типе List
не применим для аргументов (SubFoo)"
Обновление: благодаря Джеффу Си я могу изменить строку 3, чтобы сказать "new ArrayList
5 ответов
Используйте это вместо:
1 public List<? extends Foo> getFoos()
2 {
3 List<Foo> foos = new ArrayList<Foo>(); /* Or List<SubFoo> */
4 foos.add(new SubFoo());
5 return foos;
6 }
Как только вы объявите foos как List<? extends Foo>
компилятор не знает, что добавить SubFoo безопасно. Что делать, если ArrayList<AltFoo>
был назначен на foos
? Это было бы допустимым назначением, но добавление SubFoo загрязнило бы коллекцию.
Просто подумал, что я добавлю в этот старый поток, суммируя свойства параметров List, созданных с помощью типов или подстановочных знаков....
Когда у метода есть параметр / результат, который является списком, использование экземпляра типа или подстановочных знаков определяет
- Типы List, которые могут быть переданы методу в качестве аргумента
- Типы List, которые можно заполнить из результата метода
- Типы элементов, которые могут быть записаны в список в методе
- Типы, которые могут быть заполнены при чтении элементов из списка в методе
Параметр / Тип возврата: List< Foo>
- Типы List, которые могут быть переданы методу в качестве аргумента:
List< Foo>
- Типы List, которые можно заполнить из результата метода:
List< Foo>
List< ? super Foo>
List< ? super SubFoo>
List< ? extends Foo>
List< ? extends SuperFoo>
- Типы элементов, которые можно записать в список в методе:
Foo
& подтипы
- Типы, которые могут быть заполнены при чтении элементов из списка в методе:
Foo
& супертипы (доObject
)
Параметр / Тип возврата: List< ? extends Foo>
- Типы List, которые могут быть переданы методу в качестве аргумента:
List< Foo>
List< Subfoo>
List< SubSubFoo>
List< ? extends Foo>
List< ? extends SubFoo>
List< ? extends SubSubFoo>
- Типы List, которые можно заполнить из результата метода:
List< ? extends Foo>
List< ? extends SuperFoo>
List< ? extends SuperSuperFoo>
- Типы элементов, которые можно записать в список в методе:
- Никто! Не возможно добавить.
- Типы, которые могут быть заполнены при чтении элементов из списка в методе:
Foo
& супертипы (доObject
)
Параметр / Тип возврата: List<? super Foo>
- Типы List, которые могут быть переданы методу в качестве аргумента:
List< Foo>
List< Superfoo>
List< SuperSuperFoo>
List< ? super Foo>
List< ? super SuperFoo>
List< ? super SuperSuperFoo>
- Типы List, которые можно заполнить из результата метода:
List< ? super Foo>
List< ? super SubFoo>
List< ? super SubSubFoo>
- Типы элементов, которые можно записать в список в методе:
Foo
и супертипы
- Типы, которые могут быть заполнены при чтении элементов из списка в методе:
Foo
& супертипы (доObject
)
Интерпретация / Комментарий
- потребности внешних абонентов определяют дизайн объявления метода, т. е. общедоступного API (как правило, основное внимание)
- потребности внутренней логики метода определяют любые дополнительные решения относительно фактических типов данных, объявленных и сконструированных внутренне (обычно вторичное рассмотрение)
- использование
List<Foo>
если код вызывающей стороны всегда ориентирован на манипулирование классом Foo, так как это максимизирует гибкость как для чтения, так и для записи - использование
List<? extends UpperMostFoo>
если может быть много разных типов вызывающих, ориентированных на манипулирование другим классом (не всегда Foo), и в иерархии типов Foo есть единственный верхний класс, и если метод предназначен для внутренней записи в список, и манипуляция списком вызывающих чтение. Здесь метод может использовать внутреннеList< UpperMostFoo>
и добавить элементы к нему, прежде чем вернутьсяList< ? extends UpperMostFoo>
- если может быть много разных типов вызывающих, ориентированных на манипулирование другим классом (не всегда Foo), и если требуется чтение и запись в список, и в иерархии типов Foo есть единственный низший класс, то имеет смысл использовать
List< ? super LowerMostFoo>
Пытаться:
public List<Foo> getFoos() {
List<Foo> foos = new ArrayList<Foo>();
foos.add(new SubFoo());
return foos;
}
Универсальный конструктор ArrayList должен иметь определенный тип для параметризации, вы не можете использовать '?' подстановочный знак там. Изменение экземпляра на "new ArrayList
Объявление переменной 'foos' может содержать символы подстановки, но, поскольку вы знаете точный тип, имеет больше смысла ссылаться на ту же информацию о типе. То, что у вас сейчас есть, говорит о том, что foos содержит какой-то определенный подтип Foo, но мы не знаем, какой именно. Добавление SubFoo может быть запрещено, поскольку SubFoo не является "всеми подтипами Foo". Изменение объявления на "List
Наконец, я бы изменил тип возвращаемого значения на "List
Чтобы получить представление о том, как работают дженерики, посмотрите этот пример:
List<SubFoo> sfoo = new ArrayList<SubFoo>();
List<Foo> foo;
List<? extends Foo> tmp;
tmp = sfoo;
foo = (List<Foo>) tmp;
Дело в том, что он не был разработан для локальных переменных / переменных-членов, но для сигнатур функций, вот почему это так задом наперед.
Следующее будет работать нормально:
public List<? extends Foo> getFoos() {
List<Foo> foos = new ArrayList<Foo>();
foos.add(new SubFoo());
return foos;
}