У меня есть файл, List<SubClass>
который я хочу рассматривать как List<BaseClass>
. Похоже, это не должно быть проблемой, поскольку приведение a SubClass
к a BaseClass
- несложная задача , но мой компилятор жалуется, что приведение невозможно.
Итак, как лучше всего получить ссылку на те же объекты, что и объект List<BaseClass>
?
Прямо сейчас я просто создаю новый список и копирую старый:
List<BaseClass> convertedList = new ArrayList<BaseClass>(listOfSubClass)
Но насколько я понимаю, нужно создать совершенно новый список. Если возможно, мне нужна ссылка на исходный список!
java
inheritance
casting
Райли Ларк
источник
источник
Ответы:
В синтаксисе этого типа присваивания используется подстановочный знак:
Важно понимать , что
List<SubClass>
это не взаимозаменяемы сList<BaseClass>
. Код, который сохраняет ссылку на,List<SubClass>
будет ожидать, что каждый элемент в списке будетSubClass
. Если другая часть кода ссылается на список как наList<BaseClass>
, компилятор не будет жаловаться при вставкеBaseClass
илиAnotherSubClass
. Но это вызоветClassCastException
для первого фрагмента кода, который предполагает, что все в списке - этоSubClass
.Универсальные коллекции не ведут себя так же, как массивы в Java. Массивы ковариантны; то есть разрешено это делать:
Это разрешено, потому что массив «знает» тип своих элементов. Если кто-то попытается сохранить что-то, что не является экземпляром
SubClass
в массиве (черезbases
ссылку), будет выдано исключение времени выполнения.Универсальные коллекции не «знают» свой тип компонента; эта информация «стирается» во время компиляции. Следовательно, они не могут вызвать исключение времени выполнения при возникновении недопустимого хранилища. Вместо этого,
ClassCastException
когда значение считывается из коллекции , будет возникать в какой-то далекой, трудно ассоциируемой точке кода. Если вы прислушаетесь к предупреждениям компилятора о безопасности типов, вы избежите этих ошибок типа во время выполнения.источник
Эриксон уже объяснил, почему вы не можете этого сделать, но вот несколько решений:
Если вы хотите только удалить элементы из вашего базового списка, в принципе, ваш метод получения должен быть объявлен как принимающий
List<? extends BaseClass>
.Но если это не так и вы не можете его изменить, вы можете обернуть список
Collections.unmodifiableList(...)
, что позволяет вернуть List супертипа параметра аргумента. (Это позволяет избежать проблемы безопасности типов, выбрасывая исключение UnsupportedOperationException при попытках вставки.)источник
Как объяснил @erickson, если вам действительно нужна ссылка на исходный список, убедитесь, что никакой код не вставляет ничего в этот список, если вы когда-нибудь захотите снова использовать его в исходном объявлении. Самый простой способ получить его - просто привести его к простому старому неуниверсальному списку:
Я бы не рекомендовал это, если вы не знаете, что происходит со списком, и предлагал бы вам изменить любой код, который нужен для принятия списка, который у вас есть.
источник
Ниже приведен полезный фрагмент, который работает. Он создает новый список массивов, но создание объекта JVM без дополнительных затрат не имеет значения.
Я видел, что другие ответы необязательно сложные.
источник
Я пропустил ответ, в котором вы просто приводили исходный список с помощью двойного преобразования. Итак, для полноты:
Ничего не копируется, операция выполняется быстро. Однако здесь вы обманываете компилятор, поэтому вы должны быть абсолютно уверены, что не изменили список таким образом, чтобы в
subList
начале содержались элементы другого подтипа. При работе с неизменяемыми списками это обычно не проблема.источник
List<BaseClass> convertedList = Collections.checkedList(listOfSubClass, BaseClass.class)
источник
То, что вы пытаетесь сделать, очень полезно, и я считаю, что мне нужно делать это очень часто в коде, который я пишу. Пример использования:
Скажем, у нас есть интерфейс,
Foo
и у нас естьzorking
пакет,ZorkingFooManager
который создает и управляет экземплярами package-private.ZorkingFoo implements Foo
. (Очень распространенный сценарий.)Итак, он
ZorkingFooManager
должен содержать,private Collection<ZorkingFoo> zorkingFoos
но должен предоставлятьpublic Collection<Foo> getAllFoos()
.Большинство java-программистов не стали бы дважды думать перед тем,
getAllFoos()
как реализовать как выделение новогоArrayList<Foo>
, заполнение его всеми элементами изzorkingFoos
и возвращение. Мне нравится думать, что около 30% всех тактовых циклов, потребляемых java-кодом, работающим на миллионах машин по всей планете, ничего не делают, кроме создания таких бесполезных копий ArrayLists, которые собираются через микросекунды после их создания.Решением этой проблемы, конечно же, является низведение коллекции. Вот лучший способ сделать это:
Это подводит нас к
castList()
функции:Промежуточная
result
переменная необходима из-за извращения языка java:return (List<T>)list;
выдает исключение "непроверенное приведение"; Все идет нормально; но потом:@SuppressWarnings( "unchecked" ) return (List<T>)list;
является незаконным использованием аннотации подавления предупреждений.Таким образом, даже несмотря на то, что использование
@SuppressWarnings
вreturn
операторе некошерно , очевидно, что его можно использовать в присваивании, поэтому дополнительная переменная «результат» решает эту проблему. (В любом случае его следует оптимизировать либо компилятором, либо JIT.)источник
Что-то вроде этого тоже должно работать:
Не знаю, зачем нужен clazz в Eclipse ..
источник
Как насчет литья всех элементов. Он создаст новый список, но будет ссылаться на исходные объекты из старого списка.
источник
Это полный рабочий фрагмент кода, использующий Generics, для преобразования списка подклассов в суперкласс.
Вызывающий метод, который передает тип подкласса
Метод Callee, который принимает любой подтип базового класса
источник