upcast из списка <subclass>в список <superclass>через список <?> - PullRequest
13 голосов
/ 07 июля 2010

У меня есть класс A, а класс B расширяет A

В другом классе CI есть поле

private List<B> listB;

Теперь, по какой-то необычной причине, я должен реализовать этот метод вC

public List<A> getList();

Я попытался сделать это, принудительно увеличив поле списка listB в List <A> через List <?> cast:

public List<A> getList(){
    return (List<A>)(List<?>)listB;
}

Клиенты должны сделать

List<A> list = getList();
for(A a:list){
    //do something with a
}

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

Правильно ли это решение?И это лучшее решение?

Спасибо за ваши ответы.

Ответы [ 2 ]

16 голосов
/ 07 июля 2010

Нет, это не безопасно. Клиент не должен быть в состоянии сделать

List<A> list = getList();

потому что иначе они могли бы написать

list.add(new C()); // Where C extends A

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

Вы можете либо обернуть исходный список, чтобы сделать его доступным только для чтения, эффективно, либо заставить getList вернуть List<? extends A>, что означает, что клиенты все равно не смогут добавлять элементы в него.

3 голосов
/ 07 июля 2010

Проблема в том, что клиенты могут, невольно, вставлять A объекты в то, что на самом деле является списком только более конкретных B объектов:

c.getList().add(new A());

Это вызовет все виды поломок, когда ваш код попытается взять объект из списка, предполагая, что это B, но это не так.

Если ваша единственная цель - разрешить клиенту выполнять итерации по списку, лучше вместо этого выдать Iterable<A>:

public Iterable<A> getAs() { return this.theListOfAs; }

С помощью этого Iterable можно только проверять и удалять элементы, но не добавлять их.

Если вы также хотите отключить удаление, оберните List Iterable в вашей собственной реализации, выбрасывая UnsupportedOperationException при вызове remove().

...