Чтобы «исправить» код, вам нужно использовать универсальный bound :
public void interfaceIsTheArgument(List<? extends Weapon> w) { ... }
...
interfaceIsTheArgument(new ArrayList<? extends Weapon> ());
interfaceIsTheArgument(new ArrayList<Knife>());
Основная причина: List<Gun>
- это , а не подкласс List<Weapon>
. Причину этого можно проиллюстрировать следующим кодом:
List<Gun> guns = new ArrayList<Gun>();
// If List<Weapon> was a super type of List<Gun>, this next line would be allowed
List<Weapon> weapons = guns; // Won't compile, but let's assume it did
weapons.add(new Knife()); // Compiles, because Knife is a Weapon
Gun gun = guns.get(0); // Oops! guns.get(0) is a Knife, not a Gun!
Используя границу <? extends Weapon>
, мы говорим, что примем любой универсальный тип, который является подклассом из Weapon
. Использование границ может быть очень мощным. Этот вид границы является верхним пределом - мы указываем класс верхнего уровня как Weapon
.
Существует также нижняя граница , которая использует этот синтаксис:
List<? super Weapon> // accept any type that is a Weapon or higher in the class hierarchy
Итак, когда использовать каждый? Запомните это слово PECS
: «Производитель расширяется, потребитель супер». Это означает, что на стороне производителя кода (где объекты созданы ) используется extends
, а на стороне потребителя кода (где объекты используются ) us super
. Если вы попробуете это несколько раз, вы по опыту поймете, почему это работает хорошо.
Этот ТАК вопрос / ответ хорошо его освещает.