为什么将 Collections.emptySet() 与泛型一起使用在赋值中起作用,但不能用作方法参数?

2022-08-31 17:10:03

所以,我有一个带有如下构造函数的类:

public FilterList(Set<Integer> labels) {
    ...
}

我想用一个空集构造一个新对象。遵循Joshua Bloch在他的书《Effective Java》中的建议,我不想为空集创建一个新对象;我将改用:FilterListCollections.emptySet()

FilterList emptyList = new FilterList(Collections.emptySet());

这给了我一个错误,抱怨这不是一个.好吧,怎么样:java.util.Set<java.lang.Object>java.util.Set<java.lang.Integer>

FilterList emptyList = new FilterList((Set<Integer>)Collections.emptySet());

这也给了我一个错误!好的,怎么样:

Set<Integer> empty = Collections.emptySet();
FilterList emptyList = new FilterList(empty);

嘿,它的工作原理!但是为什么?毕竟,Java没有类型推断,这就是为什么如果您这样做而不是.,则会收到未经检查的转换警告。但即使在没有警告的情况下也能工作。为什么?Set<Integer> foo = new TreeSet()Set<Integer> foo = new TreeSet<Integer>()Set<Integer> empty = Collections.emptySet();


答案 1

简短的答案是 - 这是Java通用系统中类型推断的限制。它可以根据具体变量推断泛型类型,但不能根据方法参数推断泛型类型。

我怀疑这是因为方法是根据拥有对象的运行时类动态调度的,因此在编译时(当所有泛型信息都解析时),您实际上无法确定方法参数的类是什么,因此无法推断。变量声明很好,而且是恒定的,所以你可以。

其他人可能能够提供更多细节和/或一个很好的链接。:-)

在任何情况下,您始终可以为泛型调用显式指定类型参数,如下所示:

Collections.<Integer>emptySet();

甚至一次几个参数,例如

Collections.<String, Boolean>emptyMap(); // Returns a Map<String, Boolean>

这通常看起来比必须投掷更干净一些,以防推理不起作用。


答案 2

尝试

FilterList emptyList = new FilterList(Collections.<Integer>emptySet());

您可以强制具有它们的方法使用type参数,以防推理不够好,或者允许您使用子类型;例如:

// forces use of ArrayList as parameter instead of the infered List
List<String> l = someObject.<ArrayList<String> methodThatTakesTypeParamForReturnType();