Как преобразовать List <Object> в List <MyClass>

100

Это не компилируется, любое предложение приветствуется.

 ...
  List<Object> list = getList();
  return (List<Customer>) list;

Компилятор говорит: невозможно преобразовать List<Object>вList<Customer>

user198313
источник

Ответы:

157

вы всегда можете преобразовать любой объект в любой тип, предварительно преобразовав его в Object. в твоем случае:

(List<Customer>)(Object)list; 

вы должны быть уверены, что во время выполнения список не содержит ничего, кроме объектов Customer.

Критики говорят, что такое приведение означает, что с вашим кодом что-то не так; вы должны иметь возможность настроить объявления типов, чтобы этого избежать. Но дженерики Java слишком сложны и несовершенны. Иногда вы просто не знаете, есть ли красивое решение, удовлетворяющее компилятор, даже если вы очень хорошо знаете типы среды выполнения и знаете, что то, что вы пытаетесь сделать, безопасно. В этом случае просто сделайте неочищенное литье по мере необходимости, чтобы вы могли оставить работу дома.

бесспорный
источник
8
Это тоже нужно @SuppressWarnings("unchecked"). Обратите внимание, что вы также можете использовать upcast (List)вместо to (Object).
200_success
36

Это потому, что, хотя клиент является объектом, список клиентов не является списком объектов. Если да, то вы можете поместить любой объект в список клиентов.

Брайан Агнью
источник
2
Нет, это не так. Если бы вышеперечисленное было разрешено, вы бы обошли безопасность типов. Обратите внимание, что преобразование не означает создание нового списка и копирование элементов. Это означает обрабатывать единственный экземпляр как другой тип, и, таким образом, у вас будет список, который содержит потенциально не-клиентские объекты с гарантией безопасности типа, которой не должно быть. Это не имеет ничего общего с Java как таковой. Поскольку вы чувствуете, что эта функция оставляет Java в темноте, я призываю вас объяснить, как, по вашему мнению, это должно работать.
Лассе В. Карлсен
1
@ BrainSlugs83 для ваших нужд (List <Customer>) (Object) list;
Мутху Ганапати Натан
@ LasseV.Karlsen Я не говорю о кастинге, я говорю о конвертации. Это не обойдёт безопасность типов, а обеспечит её соблюдение. Реализация дженериков в Java, отсутствие перегрузок операторов, методов расширения и многих других современных удобств меня очень разочаровали. - Между тем, в .NET для этого есть два отдельных метода расширения - один вызываемый, .Cast<T>()а второй вызываемый .OfType<T>(). Первый выполняет приведение к каждому элементу (выбрасывая желаемые исключения), а второй отфильтровывает элементы, которые не могут быть преобразованы (поэтому вы можете выбрать один в зависимости от сценария использования).
BrainSlugs83
1
@EAGER_STUDENT Я бы не стал отказываться от Java, которая действительно могла бы работать (вся эта нелепая история стирания типов, мне пришлось бы попробовать ...) - но нет, я бы никогда не стал писать такой код - вы проиграете безопасность типов, что произойдет, если один элемент в коллекции не является instanceofклиентом?
BrainSlugs83
1
@ BrainSlugs83 Человек, задавший вопрос, конкретно задал вопрос о кастинге. И если в Java еще нет соответствующих методов, таких как методы .NET, на которые вы ссылаетесь (которые по-прежнему не преобразуют btw), вы легко сможете их добавить. Это все как бы ортогонально рассматриваемому вопросу, в котором задан конкретный синтаксис.
Лассе В. Карлсен
35

В зависимости от вашего другого кода лучший ответ может отличаться. Пытаться:

List<? extends Object> list = getList();
return (List<Customer>) list;

или

List list = getList();
return (List<Customer>) list;

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

Божо
источник
3
-1 - это действительно плохая привычка, и если вы не используете аннотацию «Unchecked», компилятор все равно будет жаловаться на нее
kdgregory
25

С потоками Java 8 :

Иногда подходит брутфорс:

List<MyClass> mythings = (List<MyClass>) (Object) objects

Но вот более универсальное решение:

List<Object> objects = Arrays.asList("String1", "String2");

List<String> strings = objects.stream()
                       .map(element->(String) element)
                       .collect(Collectors.toList());

Есть масса преимуществ, но одно из них состоит в том, что вы можете составить список более элегантно, если не знаете, что он содержит:

objects.stream()
    .filter(element->element instanceof String)
    .map(element->(String)element)
    .collect(Collectors.toList());
круглый
источник
1
Однако это больше похоже на копию, чем на слепок.
200_success
Кастинг, упомянутый в принятом ансере, у меня не сработал. Также я был на Java 7. Но Guava FluentIterableу меня работал.
Шридхар Сарнобат
Это то, что я искал, я просто не знал синтаксиса
Fueled By Coffee
23

Можно использовать двойной заброс.

return (List<Customer>) (List) getList();
Питер Лоури
источник
8

Обратите внимание, что я не программист на Java, но в .NET и C # эта функция называется контравариантностью или ковариацией. Я еще не углублялся в эти вещи, поскольку они являются новинкой в ​​.NET 4.0, которую я не использую, поскольку это только бета-версия, поэтому я не знаю, какой из двух терминов описывает вашу проблему, но позвольте мне описать техническая проблема с этим.

Предположим, вам разрешили кастовать. Обратите внимание: я говорю « приведение» , поскольку вы так сказали, но есть две операции, которые могут быть возможны: приведение и преобразование .

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

Вот в чем проблема.

Что произошло бы, если бы было разрешено следующее (обратите внимание, я предполагаю, что до приведения список объектов фактически содержал только объекты Customer, иначе приведение не будет работать даже в этой гипотетической версии java):

List<Object> list = getList();
List<Customer> customers = (List<Customer>)list;
list.Insert(0, new someOtherObjectNotACustomer());
Customer c = customers[0];

В этом случае это будет попытка обработать объект, который не является клиентом, как клиент, и вы получите ошибку времени выполнения в какой-то момент, либо в форме внутри списка, либо из назначения.

Однако предполагается, что Generics предоставит вам типобезопасные типы данных, такие как коллекции, и, поскольку они любят использовать слово «гарантировано», такой тип приведения с последующими проблемами не допускается.

В .NET 4.0 (я знаю, ваш вопрос касался java) это будет разрешено в некоторых очень конкретных случаях , когда компилятор может гарантировать, что выполняемые вами операции безопасны, но в общем смысле этот тип приведения не будет допустимо. То же самое и с java, хотя я не уверен в каких-либо планах по введению ко- и контравариантности в язык java.

Надеюсь, кто-то с лучшими знаниями java, чем я, сможет рассказать вам подробности будущего или реализации java.

Лассе В. Карлсен
источник
3
Будущее Java - это ... Scala. Серьезно, парень, который прикрутил дженерики к Java, разработал новый язык, отдаленно похожий на Java, но действительно очень хорошо знающий типы: очень тщательная и последовательная реализация обработки типов. Я думаю, никто не знает наверняка, какие функции Scala вернутся в Java и когда.
Carl Smotricz
отличное объяснение ковариации, которое действительно отвечает на вопрос ОП. Отлично сработано.
Кевин Дэй
Карл: Я думал, что некоторые разработчики Java решили создать C #? :) В любом случае, да, Java, скорее всего, в будущем пойдет в сторону Scala вместо, скажем, чего-то менее строго типизированного.
Esko
@Carl - в Scala есть небольшая разница в том, что по умолчанию списки неизменяемы. Так что обычно у вас нет проблемы с добавлением объекта в список клиентов, поскольку, когда вы это делаете, вы получаете новый список объектов .
Брайан Агнью,
Э ... технически это правильно - но даже до .NET 4.0 - вы могли сделать это с помощью обычных методов расширения IEnumerable (.Cast <> и .OfType <>) - так что не нужно уходить с глубокого конца, если вам просто нужна сильная итерация типа.
BrainSlugs83
7

Другой подход - использовать поток java 8.

    List<Customer> customer = myObjects.stream()
                                  .filter(Customer.class::isInstance)
                                  .map(Customer.class::cast)
                                  .collect(toList());
d0x
источник
1
спасибо, это решение такое красивое, с использованием справочника по методам
Thang
1
Никогда не думал, что кто-то прокрутит так далеко: D
d0x
3

Вам нужно просто перебирать список и приводить все объекты один за другим.

vrm
источник
3

Вы можете сделать что-то вроде этого

List<Customer> cusList = new ArrayList<Customer>();

for(Object o: list){        
    cusList.add((Customer)o);        
}

return cusList; 

Или способ Java 8

list.stream().forEach(x->cusList.add((Customer)x))

return cuslist;
Малкейт Сингх
источник
2

Вы не можете, потому что List<Object>и List<Customer>не находитесь в одном дереве наследования.

Вы можете добавить в свой List<Customer>класс новый конструктор, который принимает, List<Object>а затем перебирать список, приводя каждый Objectк a Customerи добавляя его в вашу коллекцию. Имейте в виду, что недопустимое исключение приведения может возникнуть, если вызывающий List<Object>содержит что-то, что не являетсяCustomer .

Смысл общих списков в том, чтобы ограничить их определенными типами. Вы пытаетесь взять список, в котором может быть что угодно (заказы, продукты и т. Д.), И втиснуть его в список, который может принимать только клиентов.

Роб Соберс
источник
2

Вы можете создать новый список и добавить в него элементы:

Например:

List<A> a = getListOfA();
List<Object> newList = new ArrayList<>();
newList.addAll(a);
аюшгп
источник
1

Лучше всего создать новый List<Customer>, перебрать List<Object>, добавить каждый элемент в новый список и вернуть его.

Арик Тенейк
источник
1

Как отмечали другие, вы не можете безопасно использовать их, поскольку a List<Object>не является List<Customer>. Что вы можете сделать, так это определить представление в списке, которое выполняет проверку типов на месте. Используя Коллекции Google , которые будут:

return Lists.transform(list, new Function<Object, Customer>() {
  public Customer apply(Object from) {
    if (from instanceof Customer) {
      return (Customer)from;
    }
    return null; // or throw an exception, or do something else that makes sense.
  }
});
nd.
источник
1

Аналогично с Божо выше. Здесь вы можете найти обходной путь (хотя мне это не нравится) с помощью этого метода:

public <T> List<T> convert(List list, T t){
    return list;
}

Да. Он переведет ваш список в требуемый общий тип.

В приведенном выше случае вы можете сделать такой код:

    List<Object> list = getList();
    return convert(list, new Customer());
Хендра Джая
источник
Мне нравится это решение. Даже если вам нужно добавить SuppressWarnings, лучше добавлять его в одном месте, чем при каждом небезопасном приведении.
Робсон
1

В зависимости от того, что вы хотите делать со списком, вам может даже не потребоваться преобразовывать его в файл List<Customer>. Если вы хотите только добавить Customerобъекты в список, вы можете объявить его следующим образом:

...
List<Object> list = getList();
return (List<? super Customer>) list;

Это законно (ну, не только законно, но и правильно - список имеет «некоторый супертип для клиента»), и если вы собираетесь передать его в метод, который будет просто добавлять объекты в список, то приведенный выше для этого достаточно общих оценок.

С другой стороны, если вы хотите получить объекты из списка и строго типизировать их как Customers, тогда вам не повезло, и это правильно. Поскольку список является List<Object>составным, нет никакой гарантии, что содержимое принадлежит клиентам, поэтому вам придется предоставить собственное приведение при поиске. (Или будьте действительно, абсолютно, вдвойне уверены, что список будет содержать Customersи использовать только двойное приведение из одного из других ответов, но поймите, что вы полностью обходите безопасность типов во время компиляции, которую вы получаете от дженериков в этом кейс).

Вообще говоря, всегда полезно учитывать как можно более широкие общие границы, которые были бы приемлемы при написании метода, вдвойне, если он будет использоваться в качестве библиотечного метода. Если вы собираетесь читать только из списка, используйте , например , List<? extends T>вместо List<T>- это дает вашим вызывающим абонентам гораздо больше возможностей для аргументов, которые они могут передать, и означает, что они с меньшей вероятностью столкнутся с проблемами, которых можно избежать, аналогичными тем, которые вы ' здесь.

Анджей Дойл
источник