为什么在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
来明确地显示。 是的,语法是完全时髦和非直观的。 🙂