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

Java 为什么将Collections.emptySet()与泛型一起使用可以在赋值中工作,但不能作为方法参数?,java,generics,collections,type-inference,Java,Generics,Collections,Type Inference,我有一个类,它的构造函数如下: public FilterList(Set<Integer> labels) { ... } 这给了我一个错误,抱怨java.util.Set不是java.util.Set。好的,这个怎么样: FilterList emptyList = new FilterList((Set<Integer>)Collections.emptySet()); Set<Integer> empty = Collections.emp

我有一个类,它的构造函数如下:

public FilterList(Set<Integer> labels) {
    ...
}
这给了我一个错误,抱怨
java.util.Set
不是
java.util.Set
。好的,这个怎么样:

FilterList emptyList = new FilterList((Set<Integer>)Collections.emptySet());
Set<Integer> empty = Collections.emptySet();
FilterList emptyList = new FilterList(empty);
FilterList-emptyList=new-FilterList((Set)Collections.emptySet());
这也给了我一个错误!好的,这个怎么样:

FilterList emptyList = new FilterList((Set<Integer>)Collections.emptySet());
Set<Integer> empty = Collections.emptySet();
FilterList emptyList = new FilterList(empty);
Set empty=Collections.emptySet();
FilterList emptyList=新的FilterList(空);
嘿,真管用!但是为什么呢?毕竟,Java没有类型推断,这就是为什么如果您使用
Set foo=new TreeSet()
而不是
Set foo=new TreeSet()
时会收到未检查的转换警告。但是
Set empty=Collections.emptySet()甚至可以在没有警告的情况下工作。为什么会这样?

您想这样做:

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

这告诉
emptySet
方法,它的泛型参数应该由
Integer
显式表示,而不是默认的
对象。是的,对于这一点,语法是完全时髦和不直观的

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


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

其他人可能会提供更多详细信息和/或一个不错的链接。:-)

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

Collections.<Integer>emptySet();
Collections.emptySet();
甚至可以同时使用多个参数,例如

Collections.<String, Boolean>emptyMap(); // Returns a Map<String, Boolean>
Collections.emptyMap();//返回一个映射
在推理不起作用的情况下,这看起来比强制转换要干净一些。

试试看

FilterList emptyList = new FilterList(Collections.<Integer>emptySet());
FilterList-emptyList=new-FilterList(Collections.emptySet());
在推理不够好的情况下,或者允许您使用子类型的情况下,您可以强制具有类型参数的方法使用类型参数;例如:

// forces use of ArrayList as parameter instead of the infered List
List<String> l = someObject.<ArrayList<String> methodThatTakesTypeParamForReturnType();
//强制使用ArrayList作为参数,而不是推断列表

listl=someObject。Java确实有一个类型推断,它只是非常有限。如果您有兴趣确切了解其工作原理及其局限性,请阅读以下内容:


我不知道syntex,谢谢!但我仍然想知道,为什么在变量赋值中不需要这种语法。我相信这是一个很好的解释。你不需要
new
。事实上,我不认为它会和它一起编译。我希望有人能在中标出两个正确答案:)我知道编译器在推断时不会查看上下文的方法调用,但我不知道为什么。至少对于private或
final
方法,应该可以进行推断。@Hank:或
static
方法,这些方法也在编译时解析。但是-它没有,我想这是重点。“我怀疑这是因为方法是根据所属对象的运行时类动态调度的,所以在编译时……您实际上无法确定method参数的类是什么,因此无法推断。”在这种情况下,为什么类型为cast的版本不能工作?“我怀疑这是因为方法是根据所属对象的运行时类动态调度的”。我不确定这是否是同一件事,但要调用的重载方法的哪个版本是在编译时确定的:下面所有的答案都是正确的,但我不知道的是:为什么要用空列表初始化集合,而不是调用没有参数的默认构造函数?我所知道的每个集合都有一个包含现有集合和空构造函数的构造函数。有趣的是,以下内容确实可以编译:
FilterList-emptyList=new-FilterList((Set)(Set))另一个非常有趣的示例:
Set-emptySet=(Set)Collections.emptySet();
不编译。