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());
    }