为什么在generics中使用Collections.emptySet()而不是作为方法参数?

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

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

我想用一个空集合构造一个新的FilterList对象。 遵循Joshua Bloch在他的书“有效的Java”中的build议,我不想为空集创build一个新的对象; 我只是使用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没有types推断,这就是为什么如果您执行Set<Integer> foo = new TreeSet()而不是Set<Integer> foo = new TreeSet<Integer>() ,您将获得未经检查的转换警告。 但是Set<Integer> empty = Collections.emptySet(); 甚至没有警告的作品。 这是为什么?

简短的回答是 – 这是Javagenerics系统中types推断的一个限制。 它可以根据具体variables推断genericstypes,但不能针对方法参数。

怀疑这是因为根据拥有的对象的运行时类dynamic调度方法,所以在编译时(当所有的通用信息被parsing的时候)你不能确切地知道方法参数的类是什么,无法推断。 variables声明是好的和不变的,所以你可以。

其他人可能能够提供更多的细节和/或一个不错的链接。 🙂

在任何情况下,总是可以为generics调用明确指定types参数,如下所示:

 Collections.<Integer>emptySet(); 

甚至同时有几个参数,例如

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

这种情况往往看起来比不上演还要干净一些。

尝试

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

如果推理不够好,或者允许使用子types,则可以强制使用它们的方法的types参数; 例如:

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

Java确实有types推断,只是非常有限。 如果你有兴趣知道它是如何工作的,它的局限是什么,这是一个非常好的阅读:

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

你想这样做:

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

这告诉emptySet方法,它的generics参数应该用Integer而不是默认的Object来明确地显示。 是的,语法是完全时髦和非直观的。 🙂