Подстановочный знак в списке - Generics Java

1
ArrayList<? extends A> array = new ArrayList<A>();
array.add(new A());

Почему это не скомпилировано?

  • 1
    Для дальнейшего использования, вы также должны опубликовать ошибку компилятора.
Теги:
generics

3 ответа

4

Соответствующая часть из учебника Java (http://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html):

Список, определенный списком, может быть неофициально считаться только для чтения, но это не является строгой гарантией. Предположим, у вас есть следующие два класса:

class NaturalNumber {

    private int i;

    public NaturalNumber(int i) { this.i = i; }
    // ...
}

class EvenNumber extends NaturalNumber {

    public EvenNumber(int i) { super(i); }
    // ...
}

Рассмотрим следующий код:

List<EvenNumber> le = new ArrayList<>();
List<? extends NaturalNumber> ln = le;
ln.add(new NaturalNumber(35));  // compile-time error

Поскольку List<EvenNumber> является подтипом List<? extends NaturalNumber> List<? extends NaturalNumber>, вы можете назначить le для ln. Но вы не можете использовать ln для добавления натурального числа в список четных чисел.

Возможны следующие операции в списке:

  • Вы можете добавить null.
  • Вы можете использовать clear.
  • Вы можете получить итератор и вызывать удаление.
  • Вы можете захватить подстановочный знак и записать элементы, которые вы прочитали из списка.

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

Другое соответствующее объяснение можно найти здесь (его ссылка также объясняет проблему подстановочных знаков - http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html):

...

Однако небезопасно добавлять к нему произвольные объекты:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error

Поскольку мы не знаем, что означает тип элемента c, мы не можем добавлять к нему объекты. Метод add() принимает аргументы типа E, тип элемента коллекции. Когда фактический параметр типа есть?, Это означает какой-то неизвестный тип. Любой параметр, который мы передаем для добавления, должен быть подтипом этого неизвестного типа. Поскольку мы не знаем, что это такое, мы ничего не можем передать. Единственным исключением является null, который является членом каждого типа.

С другой стороны, учитывая List, мы можем вызвать get() и использовать результат. Тип результата - неизвестный тип, но мы всегда знаем, что это объект. Поэтому безопасно назначать результат get() переменной типа Object или передавать его как параметр, где ожидается тип Object.

0

Вы не можете добавить A в List<? extends A> List<? extends A>. Чтобы исправить вашу проблему, вы должны просто объявить свой список как

ArrayList<A> array = new ArrayList<A>();

Обратите внимание, что предпочтительнее использовать интерфейс для объявления:

List<A> array = new ArrayList<A>();

Это позволяет вам легко изменить конкретный тип позднее, потому что вам нужно только сделать одно изменение.

  • 0
    В объявлении нет ничего плохого, array.add(new A()); проблема в том, что объект A нельзя добавить в List<? extends A>
  • 0
    @ Генри Хм ... Понятно. Мое решение остается в силе, даже если мое объяснение причины ошибки недопустимо.
Показать ещё 1 комментарий
0

Поскольку мы не знаем, что означает тип элемента массива, мы не можем добавлять к нему объекты.

Вместо этого используйте временный список:

ArrayList<A> tempArray = new ArrayList<A>();
tempArray.add(new A())
tempArray.add(new A())

ArrayList<? extends A> array = tempArray;

Пример использования

//Средство передвижения

public abstract class Vehicle {

}

//Автомобиль

public class Car extends Vehicle {

}

//HandlerVehicle

public class HandlerVehicle {

    private List<? extends Vehicle> _vehicles;

    public void addVehicles(List<? extends Vehicle> vehicles) {
        _vehicles = vehicles;
        //perform operations with Vehicle objects
    }

}

//HandlerCar

public class HandlerCar {
    private HandlerVehicle _handlerVehicle;
    private List<Car> _cars;

    public HandlerCar() {
        _cars = getCars();
        _handlerVehicle = new HandlerVehicle();
        _handlerVehicle.addVehicles(_cars);
    }

    private List<Car> getCars() {
        return new ArrayList<Car>();
    }
}
  • 1
    Какой смысл в последних двух строках вашего кода? Вы создаете ArrayList и назначаете его на ссылку genericArray . Затем продолжите отбрасывать новый список, назначив genericArray для ссылки на тот же список, что и array .
  • 0
    Это будет работать и больше не будет неправильным, поэтому я убрал свое пониженное голосование. Однако теперь вы можете напрямую использовать tempArray , поэтому array самом деле не нужен. Конечно, может быть, я упускаю причину сделать это.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню