Collections.emptyList()返回List <Object>?


269

我在浏览Java推断通用类型参数的规则时遇到了一些麻烦。考虑以下类,该类具有可选的list参数:

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泛型相关的问题,我强烈建议Maurice Naftalin,Philip Wadler撰写的“ Java泛型和集合 ”。
朱利安·查斯顿

Answers:


447

您遇到的问题是,即使该方法emptyList()返回了List<T>,您也没有为其提供类型,因此它默认为returning List<Object>。您可以提供type参数,并使您的代码按预期运行,如下所示:

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

现在,当您进行直接分配时,编译器可以为您找出通用类型参数。这称为类型推断。例如,如果您这样做:

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

那么该emptyList()呼叫将正确返回List<String>


12
得到它了。来自ML世界,对我来说Java不能推断正确的类型很奇怪:形式参数的类型和emptyList的返回类型显然是无法确定的。但是我猜类型推断器只能采取“婴儿步骤”。
克里斯·康威

5
在某些简单的情况下,在这种情况下,编译器似乎可以推断出缺少的type参数-但这很危险。如果该方法的多个版本存在不同的参数,则可能最终会调用错误的方法。而第二个甚至可能还不存在……
比尔·米歇尔

13
这种符号“ Collections。<String> emptyList()”确实很奇怪,但是很有意义。比Enum <E更容易扩展Enum <E >>。:)
Thiago Chaves

12
Java 8中不再需要提供类型参数(除非可能的泛型类型有歧义)。
Vitalii Fedorenko 2014年

9
第二个片段确实很好地显示了类型推断,但是不会编译。调用this必须是构造函数中的第一条语句。
Arjan

99

您要使用:

Collections.<String>emptyList();

如果您查看emptyList的来源,您会发现它实际上只是在

return (List<T>)EMPTY_LIST;

26

emptyList方法具有以下签名:

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

<T>列表”一词前的含义是它根据将结果分配给的变量类型来推断通用参数T的值。因此,在这种情况下:

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

然后,返回值由type变量显式引用List<String>,因此编译器可以找出该值。在这种情况下:

setList(Collections.emptyList());

编译器没有明确的返回变量可用于确定泛型类型,因此默认为Object

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.