Как назначить список<? расширяет BaseClass> до List<BaseClass>
Имеющий
class BaseClass implements IData ();
class ChildClassA() extends BaseClass;
class ChildClassB() extends BaseClass;
так как не может сделать
List<BaseClass> aList = new ArrayList<ChildClassA>()
так что есть
List<? extends IData> aList
for pointint to either
ArrayList<ChildClassA>(),
or
ArrayList<ChildClassB>()
aList
создается другой маршрутизацией во время выполнения, и эта часть кода имеет функцию для List<IData>
от aList
вопрос в том, если List<? extends IData> aList
это точка для ArrayList<ChildClassA>()
или же ArrayList<ChildClassB>()
,
это может сделать ListData<IData> outputList = (List<IData>) aList
? что-то вроде ниже:
(кажется, это работает, но не уверен, есть ли лучший способ назначить массив обобщений, кроме приведения.)
Редактировать: вывод List<IData> outputList
предназначен только для чтения (неизменяемый), без вставки / удаления для него, он будет только повторять IData, чтобы реагировать на то, чем в действительности является IData.
List<? extends IData> aList = new ArrayList<ChildClassA>();
ListData<IData> outputList = (List<IData>)aList
List<? extends IData> aList = new ArrayList<ChildClassB>();
ListData<IData> outputList = (List<IData>)aList
2 ответа
TL; Dr Использование Collections#unmodifiableList
:
List<IData> outputList = Collections.unmodifiableList(aList);
Для получения дополнительной информации по этой теме вы можете ознакомиться с принципом PECS.
Это невозможно, потому что эти два типа несовместимы.
List<BaseClass>
это только то, что объявлено, список BaseClass
объекты. Точнее, это дает две гарантии:
- объекты, извлеченные из него, могут быть назначены
BaseClass
- каждый объект, который может быть назначен
BaseClass
может быть добавлено к нему (и никакому другому)
List<? extends BaseClass>
это более свободная декларация. Точно, это просто не дает второй гарантии. Однако не только гарантия исчезла, но и теперь невозможно добавить элементы в нее, поскольку точный общий тип списка не определен. Это может даже измениться для того же объявления списка (не того же самого объекта списка) во время выполнения.
Как следствие, List<? extends BaseClass>
не присваивается List<BaseClass>
, поскольку последний дает гарантию, первый не может выполнить.
На практике рассмотрим следующий метод:
public List<BaseClass> makeList() {
// TODO implement method
return null;
}
Если кто-то реализует этот метод, возвращая List<? extends BaseClass>
клиент, использующий этот метод, не сможет добавить элементы к нему, хотя в его объявлении указано иное.
Из-за этого такое назначение приводит к ошибке компиляции.
Чтобы исправить проблему, приведенную в примере, к методу может быть добавлено свободное объявление:
public List<? extends BaseClass> makeList() {
// TODO implement method
return null;
}
Это будет сигнализировать каждому клиенту, что список, возвращаемый этим методом, не предназначен для добавления элементов в него.
Теперь вернемся к вашему варианту использования. На мой взгляд, наиболее подходящим решением будет перефразировать функцию, которая
возьмите [s] список из списка a List.
Как кажется, в настоящее время он объявлен как
public void useList(List<BaseClass> list);
но поскольку он не добавляет элементы в список, он должен быть объявлен как
public void useList(List<? extends BaseClass> list);
Однако, если этот метод является частью неизменяемого в настоящее время API, вы все равно можете сделать:
List<? extends BaseClass> list;
....
List<BaseClass> tmp = Collections.unmodifiableList(list);
useList(tmp);
Нет, это небезопасно.
После этого произнесения было бы законно добавить в список, который должен содержать только ChildClassA
типизированные элементы, элементы другого дочернего типа ChildClassB
типа и наоборот.
Мы можем просто немного добавить ваш код, чтобы сделать более понятным, почему это не должно быть разрешено:
List<ChildClassA> aList = new ArrayList<ChildClassA>();
aList.add(a1);
aList.add(a2);
//...
List<IData> iDataList = (List<IData>) aList;
iDataList.add(b1);
iDataList.add(b2);
//...
for (ChildClassA a : aList) {
// a some point a is going to be assigned b1 or b2 and they results in cast
// exception.
}
Обратите внимание, что iDataList
делает ссылку на тот же объект списка, что и aList
, Если это приведение разрешено, вы сможете добавить элементы aList
это не ChildClassA
экземпляров.
Лучшее решение на деталях.
Если проблема в том, что сторонняя библиотека требует List<IData>
типизированная ссылка и до тех пор, пока она предназначена только для чтения, вы можете использовать неизменяемый прокси, возвращаемый Collections.unmodifiableList
:
import java.util.Collections;
//...
final List<ChildClassA> aList = new ArrayList<>();
//... add stuff to aList
final List<IData> readOnlyIDataList = Collections.unmodifiableList(aList);
//... read only access operations readOnlyIDataList