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

Chr*_*way 260 java generics type-inference

我在导航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;
  }
}
Run Code Online (Sandbox Code Playgroud)

我的Java编译器给出以下错误:

Person.java:9: The constructor Person(String, List<Object>) is undefined
Run Code Online (Sandbox Code Playgroud)

但是Collections.emptyList()返回类型<T> List<T>,而不是List<Object>.添加演员表无济于事

public Person(String name) {
  this(name,(List<String>)Collections.emptyList());
}
Run Code Online (Sandbox Code Playgroud)

产量

Person.java:9: inconvertible types
Run Code Online (Sandbox Code Playgroud)

使用EMPTY_LIST而不是emptyList()

public Person(String name) {
  this(name,Collections.EMPTY_LIST);
}
Run Code Online (Sandbox Code Playgroud)

产量

Person.java:9: warning: [unchecked] unchecked conversion
Run Code Online (Sandbox Code Playgroud)

而以下更改会使错误消失:

public Person(String name) {
  this.name = name;
  this.nicknames = Collections.emptyList();
}
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释我在这里遇到的类型检查规则,以及解决它的最佳方法吗?在这个例子中,最终的代码示例是令人满意的,但是对于更大的类,我希望能够在没有重复代码的情况下编写遵循这个"可选参数"模式的方法.

额外信用:什么时候适合使用EMPTY_LIST而不是emptyList()

小智 436

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

public Person(String name) {
  this(name,Collections.<String>emptyList());
}
Run Code Online (Sandbox Code Playgroud)

现在,当您进行直接赋值时,编译器可以为您计算泛型类型参数.它被称为类型推断.例如,如果你这样做:

public Person(String name) {
  List<String> emptyList = Collections.emptyList();
  this(name, emptyList);
}
Run Code Online (Sandbox Code Playgroud)

然后emptyList()调用将正确返回a List<String>.

  • 得到它了.来自ML世界,我很奇怪Java无法推断出正确的类型:形式参数的类型和emptyList的返回类型显然是可以统一的.但我猜类型推断器只能采取"小步骤". (12认同)
  • 这个符号"Collections.<String> emptyList()"真的很奇怪,但是有道理.比Enum <E扩展Enum <E >>更容易.:) (11认同)
  • 在Java 8中不再需要提供类型参数(除非可能的泛型类型存在歧义). (11认同)
  • 第二个片段确实很好地显示了类型推断,但当然不会编译.对`this`的调用必须是构造函数中的第一个语句. (9认同)
  • 在一些简单的情况下,编译器似乎可以在这种情况下推断缺少的类型参数 - 但这可能是危险的.如果该方法的多个版本存在不同的参数,您最终可能会调用错误的版本.而第二个甚至可能还不存在...... (5认同)
  • @ChrisConway 有一个“软件工程”论点反对聪明的类型推断器,尤其是那些跨越方法或模块边界进行推理的推断器。类型是接口的一部分,如果你不明确地写下来,它可能会随着时间的推移而不可预测地漂移。您不能编写推断类型(它们往往对人类不友好,有时甚至无法用语言表达,它们在“所有这些仍然有效”的意义上是“最大的”,而不是在以人为中心的“这个”中的“最小”是我们正在构建的,我们将测试的,我们关心的”。) (2认同)

car*_*son 95

你想用:

Collections.<String>emptyList();
Run Code Online (Sandbox Code Playgroud)

如果你看一下emptyList的来源,你会发现它实际上只是做了一个

return (List<T>)EMPTY_LIST;
Run Code Online (Sandbox Code Playgroud)


Dan*_*ton 26

emptyList方法有这个签名:

public static final <T> List<T> emptyList()
Run Code Online (Sandbox Code Playgroud)

<T>字之前列表意味着它推断被分配给从可变结果的类型的一般参数T的值.所以在这种情况下:

List<String> stringList = Collections.emptyList();
Run Code Online (Sandbox Code Playgroud)

然后,返回值由类型的变量显式引用List<String>,因此编译器可以解决它.在这种情况下:

setList(Collections.emptyList());
Run Code Online (Sandbox Code Playgroud)

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