Як передати список <Object> до списку <MyClass>


99

Це не складається, будь-яка пропозиція оцінена.

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

Компілятор каже: не може кинути List<Object>вList<Customer>


Відповіді:


156

ви завжди можете передавати будь-який об’єкт будь-якому типу, попередньо додавши його до Object спочатку. у вашому випадку:

(List<Customer>)(Object)list; 

Ви повинні бути впевнені, що під час виконання список не містить нічого, крім об'єктів Клієнта.

Критики кажуть, що таке кастинг вказує на те, що у вашому коді щось не так; ви повинні мати змогу налаштувати декларації типу, щоб уникнути цього. Але дженерики Java занадто складні, і вона не є досконалою. Іноді ви просто не знаєте, чи є гарне рішення для задоволення компілятора, хоча ви дуже добре знаєте типи виконання та знаєте, що намагаєтесь зробити, це безпечно. У цьому випадку просто виконайте сирий кастинг за потребою, щоб ви могли залишити роботу вдома.


8
У цьому теж потрібно @SuppressWarnings("unchecked"). Зауважте, що (List)замість цього ви також можете поновитись (Object).
200_успіх

36

Це тому, що, хоча замовник є об'єктом, список клієнтів - це не перелік об'єктів. Якщо це було, то ви можете помістити будь-який об’єкт у список клієнтів.


2
Ні, це не так. Ви б обходили безпеку типу, якби це було дозволено. Зауважте, що кастинг не означає створити новий список і скопіювати елементи. Це означає, що обробляти один екземпляр як інший тип, і, таким чином, у вас з'явиться список, який містить об'єкти, які потенційно не є клієнтами, з гарантією безпеки типу, яка не повинна. Це не має нічого спільного з Java як такою. Оскільки ви відчуваєте, що ця функція залишає Яву застряглою в темні століття, я закликаю вас пояснити, як ви вважаєте, що це має працювати замість цього.
Лассе В. Карлсен

1
@ BrainSlugs83 для вашої потреби (Список <Користувач>) (Об'єкт) список;
Муту Ганапатій Натан

@ LasseV.Karlsen Я не говорю про кастинг, я про перетворення. Це не обходило б безпеку типу, а застосовуватиме її. Реалізація дженерики Java, відсутність перевантаження операторів, методи розширення та багато інших сучасних зручностей залишили мене епічно розчарованим. - Тим часом, .NET має для цього два окремі методи розширення - один викликаний .Cast<T>()та один викликаний .OfType<T>(). Перший виконує трансляцію кожного елемента (викидаючи потрібні винятки), тоді як другий фільтрує елементи, які не можуть бути передані (так що ви виберете його залежно від сценарію використання).
BrainSlugs83

1
@EAGER_STUDENT Я б не ставив перед Явою, що це насправді може спрацювати (весь цей смішний тип стирання бізнесу, я б спробував це ...) - але ні, я ніколи не писав би такий код - ти програєш тип безпеки, що станеться, якщо один елемент колекції не є 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 - це дійсно погана звичка потрапляти, і якщо ви не використовуєте анотацію "Не перевірено", компілятор все одно буде скаржитися на це
kdgregory

24

З потоками 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_успіх

Кастинг, згаданий у прийнятому ансер, для мене не працював. Також я був на Java 7. Але Гуава FluentIterableпрацював на мене.
Шрідхар Сарнобат

Це те, що я шукав, я просто не знав синтаксису
Заправляється кавою

23

Можна використовувати подвійний акторський склад.

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

8

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

Давайте припустимо, що вам дозволили робити кастинг. Зауважте, я кажу в ролі , оскільки це ви сказали, але можливі дві операції: кастинг і перетворення .

Перетворення означало б, що ви отримуєте новий об’єкт списку, але ви кажете, що означає, що ви хочете тимчасово ставитися до одного об’єкта як до іншого типу.

Ось проблема з цим.

Що буде, якби було дозволено наступне (зауважте, я припускаю, що перед складом список об’єктів насправді містить лише об'єкти клієнта, інакше кастинг не працюватиме навіть у цій гіпотетичній версії Java):

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

У цьому випадку це намагатиметься розглядати об'єкт, який не є замовником, як замовником, і ви отримаєте помилку виконання в один момент, або форму всередині списку, або за призначенням.

Однак, дженерики повинні надавати вам безпечні для типу типи даних, як колекції, і оскільки вони люблять кидати слово "гарантовано" навколо, цей тип трансляції з подальшими проблемами не допускається.

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

Сподіваємось, хтось із кращими знаннями Java, ніж я, може розповісти вам про особливості майбутнього або впровадження Java.


3
Майбутнє Яви - це ... Скала. Серйозно, хлопець, який підключив дженерики на Java, розробив нову мову, нечітко схожу на Java, але по-справжньому, дуже добре володіє типами: Дуже ретельна та послідовна реалізація обробки типів. Я думаю, ніхто точно не знає, які функції Scala перейдуть назад у Java, і коли.
Карл Смотрич

відмінне пояснення коваріації, яке справді відповідає на питання ОП. Молодці.
День Кевіна

Карл: Я думав, що деякі Java-розробники пішли вперед, щоб створити C #? :) Так чи інакше, Java, швидше за все, піде в напрямку Скали в майбутньому замість, скажімо, чогось менш сильного набраного.
Есько

@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());

1
дякую, це рішення таке прекрасне, використовуючи метод посилання
thang

1
Ніколи не думав, що хтось прокрутить вниз так далеко: D
d0x

3

Вам слід просто перебрати список і передати всі об'єкти по одному


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з а, Customerі додаючи його до своєї колекції. Будьте в курсі, що недійсне виключення в програмі може статися, якщо абонент List<Object>містить щось, що не є Customer.

Суть загальних списків полягає в обмеженні їх певними типами. Ви намагаєтесь взяти список, у якому може бути що завгодно (Замовлення, Товари тощо) та видавити його до списку, який можуть приймати лише Клієнти.


2

Ви можете створити новий Список і додати в нього елементи:

Наприклад:

List<A> a = getListOfA();
List<Object> newList = new ArrayList<>();
newList.addAll(a);

1

Ваша найкраща ставка - створити новий List<Customer>, повторити його List<Object>, додати кожен елемент до нового списку та повернути його.


1

Як зазначали інші, ви не можете їх ощадно віддати, оскільки 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.
  }
});

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;

Це законно (ну, не просто юридично, але правильно - список має "деякий супертип для Клієнта"), і якщо ви збираєтеся передати його в метод, який буде просто додавати об'єкти до списку, то вище родових меж для цього достатньо.

З іншого боку, якщо ви хочете отримати об'єкти зі списку та набрати їх напевно як Клієнти - тоді вам не пощастить, і це правильно. Оскільки у списку List<Object>немає гарантії, що вміст є клієнтами, тому вам доведеться надати власний кастинг для пошуку. (Або по-справжньому, абсолютно, вдвічі впевнений, що список буде містити Customersта використовувати лише подвійний переклад з однієї з інших відповідей, але розумійте, що ви повністю обходите безпеку типу компіляції, яку ви отримуєте від дженериків у цьому випадок).

Загалом, завжди добре враховувати найширші загальні межі, які були б прийнятні при написанні методу, вдвічі, якщо це буде використовуватися як бібліотечний метод. Якщо ви збираєтеся читати лише зі списку, використовуйте List<? extends T>замість List<T>, наприклад, це дає вашим абонентам набагато більше сфери в аргументах, які вони можуть передавати, і означає, що вони рідше стикаються з проблемами, які можна уникнути, аналогічно тому, яке ви ' є тут.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.