Java Generics WildCard Вопрос: Список <? расширяет A>
Скажем, у меня есть эти классы: автомобиль, автомобиль и космический корабль:
class Vehicle{
void rideVehicle(Vehicle v){
System.out.println("I am riding a vehicle!");
}
}
class Car extends Vehicle{
void rideVehicle(Vehicle c){
System.out.println("I am riding a car!");
}
}
class SpaceShip extends Vehicle{
void rideVehicle(Vehicle c){
System.out.println("I am riding a spaceship!");
}
}
и я пишу этот метод addCars:
private static void addCars(List<? extends Vehicle> vcls){
vcls.add(new Car());
vcls.add(new Car());
vcls.add(new Car());
}
Почему я получаю ошибку времени компиляции?? Я понимаю, что List является супертипом List для любого X, который расширяет Vehicle. не так ли?
Спасибо
Изменить: ошибка, которую я получаю (время компиляции): метод add (capture # 2-of? extends Vehicle) в типе List не применим для аргументов (Car).
Ответы
Ответ 1
Аргументы аргумента контравариантны в подтипе, и по определению подстановочного символа для каждого типа T
, который продолжается Vehicle
Foo<T>
, является подтипом Foo<* extends Vehicle>
. Следствием этого является то, что подстановочные знаки великолепны, когда вы только заботитесь о типе возвращаемого значения, но не работаете в таких ситуациях, когда хотите передать значение типа методу.
Проблема заключается в том, что пользователь может попытаться вызвать
List<SpaceShip> l = ...
addCars(l);
если ваш код должен был компилироваться, l
тогда будет списком космических кораблей, содержащих 3 машины. Ясно, что ничего хорошего.
Ответ 2
Вот указатель на то, почему вы получаете ошибку компиляции. В частности,
Список - пример ограниченный подстановочный знак.? означает неизвестный тип, как и подстановочные знаки, которые мы видели ранее. Однако в в этом случае мы знаем, что это неизвестное тип на самом деле является подтипом Shape. (Примечание: это может быть сама форма или некоторый подкласс; это не обязательно Расширьте форму.) Мы говорим, что Shape is верхняя граница шаблона.
Как обычно, цена должна быть оплачена для гибкости использования подстановочные знаки. Эта цена заключается в том, что она теперь незаконно писать в формы в тело метода. Например, это не разрешено:
public void addRectangle(List<? extends Shape> shapes) {
shapes.add(0, new Rectangle()); // Compile-time error!
}
Вы должны быть в состоянии выяснить, почему код выше не допускается. Тип второго параметр to shapes.add() есть? продолжается Форма - неизвестный подтип формы. Поскольку мы не знаем, какой тип он есть, мы не знаем, является ли это супертипом Прямоугольник; это может быть или не быть такой супертип, поэтому небезопасно пропустите там прямоугольник.
Ответ 3
private static void addCars(List<? extends Vehicle> vcls){
должен быть
private static void addCars(List<? super Vehicle> vcls){
Это устранит ошибку времени компиляции.
EDIT: прочитайте здесь.
Ответ 4
Представленный список - это список определенного типа транспортного средства (где для аргумента мы будем ссылаться на тип как T
), но этот конкретный тип T
неизвестен; это может быть List<Vehicle>
, List<Car>
и т.д. Поэтому, поскольку конкретный общий тип списка неизвестен, не разрешается вызывать любой метод, для которого требуется конкретный T
в качестве аргумента. Только методы, которые не включают T
в качестве аргумента, могут быть вызваны.
Практический результат этого, в случае с List, заключается в том, что это предотвращает добавление чего-либо в список - список не доступен для записи. С другой стороны, список можно прочитать, но с возвращенными объектами, известными как Vehicle
.
То есть неизвестный тип T
не может быть предоставлен в список, но его известный суперкласс из Vehicle
может быть возвращен списком.
В качестве примера, учитывая ваш метод:
private static void addCars(List<? extends Vehicle> vcls) {
вы могли бы вызвать вызов:
List<Car> cars=new ArrayList<Car>();
addCars(cars);
который вы должны разрешить. Однако, поскольку addCars
знает список только как "некоторый подтип Vehicle
", нельзя добавлять объекты в список, поскольку следующий вызов будет таким же правильным:
List<Spaceship> ships=new ArrayList<Spaceship>();
addCars(ships);
в результате чего становится ясно, что ошибка должна заключаться в попытке добавить объекты Car в список под видом списка объектов Vehicle
.
Ответ 5
Тип параметра ? extends Vehicle
, что означает неизвестный подтип
of Vehicle
. Так как мы не знаем, что это такое, мы не знаем, является ли это супертипом
of Car
; это может быть или не быть таким супертипом, поэтому безопасно передавать
Car
там.
Прочтите страницу 7 этот учебник.
Ответ 6
Когда вы говорите <? extends Vehicle>
, это означает, что он может быть любого типа, который расширяет транспортное средство. Это означает, что кто-то может передать List, и он примет его. Теперь List<Spaceship>
не может иметь новый Car() как один из его элементов. Поэтому, чтобы избежать этих ошибок, вам не разрешено добавлять какой-либо объект в список, если вы использовали выражение подстановочного символа.
Ответ 7
Вы можете использовать:
private static void addCars(List<? super Vehicle> vcls)
(это означает, что вызывающий объект должен передать список объектов, которые являются транспортным средством или супер-типом)
или
private static void addCars(List<Vehicle> vcls)
Ответ 8
если возможно, возможно.
private static void addCars(List<? extends Vehicle> vcls){
vcls.add(new Car());
vcls.add(new Car());
vcls.add(new Car());
}
то вы можете вызвать addCars таким образом:
List<SpaceShip> ships = new ArrayList<SpaceShip>();
addCars(ships);
Ответ 9
Проблема принципа Get и Put:
вы можете попробовать это
private static void addCars(List<? super Car> vcls){
vcls.add(new Car());
vcls.add(new Car());
vcls.add(new Car());
его как;
List<Integer> ints=Arrays.asList(1,2,3);
List<? extends Number> nums=ints;
double dbl=sum(nums); // ===ok
nums.add(3.14); //===compile-time-error
и для подстановочных знаков для List<Object> ints=Arrays<Object>.asList(1,"two");
List<? super Integer> nums=ints;
double dbl=sum(nums); // ===compile-time-error
nums.add(3.14); //===ok
Ответ 10
Используя принца, получите и положите в подстановочный знак
Если подстановочный знак с расширением --- > Использование метода get
Если подстановочный знак с помощью метода Super ---- > Using put
Здесь вы хотите добавить значение в List (значение put method). Вы можете изменить код
List<? extends Vehicle become List<? super Vehicle> then it will compile legally
private static void addCars(List<? super Vehicle> vcls){
vcls.add(new Car());
vcls.add(new Car());
vcls.add(new Car());
}