为什么使用带有泛型的Collections.emptySet()可以在赋值中工作但不能用作方法参数?

时间:2010-06-17 13:05:02

标签: java generics collections type-inference

所以,我有一个带有这样的构造函数的类:

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

我想用空集构造一个新的FilterList对象。按照Joshua Bloch在他的“Effective Java”一书中的建议,我不想为空集创建一个新对象;我只会使用Collections.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();在没有警告的情况下工作。那是为什么?

4 个答案:

答案 0 :(得分:116)

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

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

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

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

Collections.<Integer>emptySet();

甚至可以同时使用几个参数,例如

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

在推理不起作用的情况下,这通常看起来比必须更清洁。

答案 1 :(得分:7)

尝试

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

答案 2 :(得分:5)

你想这样做:

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

告诉emptySet方法,其通用参数应由Integer显式显示,而不是默认Object。是的,语法完全是时髦且非直观的。 :)

答案 3 :(得分:5)

Java确实有类型推断,它只是非常有限。如果您有兴趣了解它的确切工作原理及其局限性,那么这是一个非常好的读物:

http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html#Type%2BArgument%2BInference