Collections.emptyList () повертає список <Object>?


269

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

import java.util.Collections;
import java.util.List;

public class Person {
  private String name;
  private List<String> nicknames;

  public Person(String name) {
    this(name,Collections.emptyList());
  }

  public Person(String name,List<String> nicknames) {
    this.name = name;
    this.nicknames = nicknames;
  }
}

Мій компілятор Java видає таку помилку:

Person.java:9: The constructor Person(String, List<Object>) is undefined

Але Collections.emptyList()тип повернення <T> List<T>, ні List<Object>. Додавання амплуа не допомагає

public Person(String name) {
  this(name,(List<String>)Collections.emptyList());
}

врожайність

Person.java:9: inconvertible types

Використання EMPTY_LISTзамістьemptyList()

public Person(String name) {
  this(name,Collections.EMPTY_LIST);
}

врожайність

Person.java:9: warning: [unchecked] unchecked conversion

Враховуючи, що наступна зміна призводить до помилки:

public Person(String name) {
  this.name = name;
  this.nicknames = Collections.emptyList();
}

Хто-небудь може пояснити, яке правило перевірки типу я тут наштовхую, і найкращий спосіб обійти це? У цьому прикладі підсумковий приклад коду є задовільним, але з більшими класами я хотів би мати можливість записувати методи, керуючись цим шаблоном "необов'язковий параметр" без дублювання коду.

Для додаткового кредиту: коли це доцільно використовувати EMPTY_LISTна відміну від emptyList()?


1
Для всіх питань, пов’язаних з Java Generics, я настійно рекомендую " Дженерики та колекції Java " Моріса Нафталіна, Філіпа Вадлера.
Жульєн Частанг

Відповіді:


447

Проблема, з якою ви стикаєтесь, полягає в тому, що, хоча метод emptyList()повертається List<T>, ви не надали його типу, тому він за замовчуванням повертається List<Object>. Ви можете надати параметр типу та вести, як ваш код поводиться так, як очікувалося:

public Person(String name) {
  this(name,Collections.<String>emptyList());
}

Тепер, коли ви робите пряме завдання, компілятор може визначити параметри загального типу для вас. Це називається виведенням типу. Наприклад, якщо ви зробили це:

public Person(String name) {
  List<String> emptyList = Collections.emptyList();
  this(name, emptyList);
}

тоді emptyList()виклик правильно поверне a List<String>.


12
Зрозумів. Походячи із світу ML, мені дивно, що Java не може зробити висновок правильного типу: тип формального параметра та тип повернення emptyList чітко не уніфіковані. Але я здогадуюсь, тип inferr може робити лише "кроки дитини".
Кріс Конвей

5
У деяких простих випадках компілятору може здатися можливим зробити параметр відсутнього типу в цьому випадку - але це може бути небезпечно. Якщо існували декілька версій методу з різними параметрами, ви можете в результаті викликати неправильну. А друга, можливо, ще не існує ...
Білл Мішелл

13
Це позначення "Колекції. <String> emptyList ()" насправді дивне, але має сенс. Легше, ніж Enum <E розширює Enum <E>>. :)
Тіаго Чавес

12
Подання параметру типу більше не потрібно в Java 8 (якщо немає неоднозначності в можливих загальних типах).
Віталій Федоренко

9
Другий фрагмент добре показує висновок про тип, але, звичайно, не збирається. Заклик до thisповинен бути першим твердженням у конструкторі.
Ар'ян

99

Ви хочете використовувати:

Collections.<String>emptyList();

Якщо ви подивитесь на джерело, який порожній список ви бачите, що він насправді просто робить

return (List<T>)EMPTY_LIST;

26

метод emptyList має такий підпис:

public static final <T> List<T> emptyList()

Це <T>перед словом List означає, що воно визначає значення загального параметра T з типу змінної, якій присвоюється результат. Отже, у цьому випадку:

List<String> stringList = Collections.emptyList();

Потім на повернене значення явно посилається змінна типу List<String>, тому компілятор може розібратися в цьому. В цьому випадку:

setList(Collections.emptyList());

Немає явної змінної повернення, яку компілятор може використовувати для з'ясування загального типу, тому він за замовчуванням Object.

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