为啥将 Collections.emptySet() 与泛型一起使用在赋值中而不是作为方法参数?

     2023-02-25     256

关键词:

【中文标题】为啥将 Collections.emptySet() 与泛型一起使用在赋值中而不是作为方法参数?【英文标题】:Why does using Collections.emptySet() with generics work in assignment but not as a method parameter?为什么将 Collections.emptySet() 与泛型一起使用在赋值中而不是作为方法参数? 【发布时间】:2011-03-04 23:02:57 【问题描述】:

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

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

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

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

这给了我一个错误,抱怨java.util.Set&lt;java.lang.Object&gt; 不是java.util.Set&lt;java.lang.Integer&gt;。好的,这个怎么样:

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

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

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

嘿,它有效!但为什么?毕竟,Java 没有类型推断,这就是为什么如果你使用Set&lt;Integer&gt; foo = new TreeSet() 而不是Set&lt;Integer&gt; foo = new TreeSet&lt;Integer&gt;(),你会得到一个未经检查的转换警告。但是Set&lt;Integer&gt; empty = Collections.emptySet(); 工作时甚至没有警告。这是为什么呢?

【问题讨论】:

以下所有答案都是正确的,但我不明白的是:为什么要使用 emptyList 初始化集合而不是调用不带参数的默认构造函数?我知道的每个集合都有一个带有现有集合的构造函数和一个空的构造函数。 有趣的是,以下代码可以编译:FilterList emptyList = new FilterList((Set&lt;Integer&gt;)(Set&lt;? extends Object&gt;)Collections.emptySet()); 另一个非常有趣的例子:Set&lt;Integer&gt; emptySet = (Set&lt;Integer&gt;)Collections.emptySet(); 无法编译。 【参考方案1】:

你想这样做:

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

这告诉emptySet 方法它的泛型参数应该显式地使用Integer 而不是默认的Object。是的,语法是完全时髦且不直观的。 :)

【讨论】:

我不知道那个语法,谢谢!但我仍然想知道为什么在变量赋值中不需要这种语法。 查看 Andrzej Doyle 的回答。我相信这是一个很好的解释。 那里不需要new。事实上,我认为它不会与它一起编译。【参考方案2】:

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

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

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

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

Collections.<Integer>emptySet();

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

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

在无法进行推理的情况下,这通常看起来比强制转换要干净一些。

【讨论】:

+1 很好地解释了原因。我希望可以在 SO 中将两个答案标记为正确 :) 我知道编译器在推断时不会查看方法调用的上下文,但我不确定为什么。至少对于私有或final 方法,应该可以进行推理。 @Hank: 或 static 方法,它们也在编译时解析。仍然 - 它没有,我想这是重点。 "我怀疑这是因为方法是根据拥有对象的运行时类动态调度的,所以在编译时......你实际上无法确定方法参数的类将是,因此无法推断。”在那种情况下,为什么类型转换的版本不起作用? “我怀疑这是因为方法是根据拥有对象的运行时类动态调度的”。我不确定它是否是同一件事,但是在编译时确定要调用哪个版本的重载方法:ampersand.space/blog/2007/1/12/java-overload【参考方案3】:

试试

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

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

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

【讨论】:

【参考方案4】:

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

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

【讨论】:

为啥 math.inf 是浮点数,为啥我不能将其转换为整数?

】为啥math.inf是浮点数,为啥我不能将其转换为整数?【英文标题】:Whyismath.infafloatandwhycan\'tIconvertittoaninteger?为什么math.inf是浮点数,为什么我不能将其转换为整数?【发布时间】:2019-05-0223:32:18【问题描述】:我正在做一些实... 查看详情

为啥将“viewWithTag”与“dequeueReusableCellWithIdentifier”一起使用?

】为啥将“viewWithTag”与“dequeueReusableCellWithIdentifier”一起使用?【英文标题】:Whyuse"viewWithTag"with"dequeueReusableCellWithIdentifier"?为什么将“viewWithTag”与“dequeueReusableCellWithIdentifier”一起使用?【发布时间】:2010-09... 查看详情

为啥将局部变量推回 Vectorworks

】为啥将局部变量推回Vectorworks【英文标题】:WhyDoesPushingBackLocalVariabletoVectorWorks为什么将局部变量推回Vectorworks【发布时间】:2019-01-2401:58:00【问题描述】:C++向量存储指向它存储的值的指针(即整数向量将存储指向整数的指针... 查看详情

为啥**不**将函数声明为`constexpr`?

】为啥**不**将函数声明为`constexpr`?【英文标题】:Whyever**not**declareafunctiontobe`constexpr`?为什么**不**将函数声明为`constexpr`?【发布时间】:2011-07-0323:52:43【问题描述】:任何只包含return语句的函数都可以被声明constexpr因此将允... 查看详情

为啥我不能将属性转换为嵌套元素?

】为啥我不能将属性转换为嵌套元素?【英文标题】:Whycan\'tIconvertattributetonestedelement?为什么我不能将属性转换为嵌套元素?【发布时间】:2012-01-0608:10:30【问题描述】:我正在从“App.config”读取设置。我刚刚弄清楚如何使用Co... 查看详情

为啥不能将窗体的大小绑定到 ApplicationSettings?

】为啥不能将窗体的大小绑定到ApplicationSettings?【英文标题】:Whycan\'tyoubindtheSizeofawindowsformtoApplicationSettings?为什么不能将窗体的大小绑定到ApplicationSettings?【发布时间】:2010-09-0607:46:31【问题描述】:更新:已解决,带有代... 查看详情

为啥我无法将变量设置为无 [关闭]

】为啥我无法将变量设置为无[关闭]【英文标题】:WhyamInotabletosetavariableasnone[closed]为什么我无法将变量设置为无[关闭]【发布时间】:2016-06-1914:54:51【问题描述】:好的,所以我尝试将变量设置为None,以便我可以清除它,但每... 查看详情

为啥 registerForContextMenu 将 onCreateContextMenu 传递给 null ContextMenuInfo?

】为啥registerForContextMenu将onCreateContextMenu传递给nullContextMenuInfo?【英文标题】:WhyisregisterForContextMenupassingonCreateContextMenuanullContextMenuInfo?为什么registerForContextMenu将onCreateContextMenu传递给nullContextMenuInfo?【发布 查看详情

为啥不使用 auditctl 将手表插入***目录?

】为啥不使用auditctl将手表插入***目录?【英文标题】:whynotinsertawatchtothetopleveldirectorywithauditctl?为什么不使用auditctl将手表插入***目录?【发布时间】:2017-08-0412:34:59【问题描述】:通过man手册,我知道了一些关于auditctl的信息... 查看详情

为啥将 ngStyle 指令声明到 [] 中?

】为啥将ngStyle指令声明到[]中?【英文标题】:WhythengStyledirectiveisdeclaredintothe[]?为什么将ngStyle指令声明到[]中?【发布时间】:2017-11-1107:15:25【问题描述】:我是Angular2的绝对初学者,我对ngStyle指令的正确语法有以下疑问。我有... 查看详情

为啥将整数插入 SQLite 会爆炸?

】为啥将整数插入SQLite会爆炸?【英文标题】:WhyinsertinganIntegerintoSQLitewouldbombout?为什么将整数插入SQLite会爆炸?【发布时间】:2012-01-2620:01:24【问题描述】:我使用FMDBexecuteUpdate在下面的插入语句中获得了EXC_BAD_ACCESS。它发生在... 查看详情

GetFunctionPointerForDelegate 将委托中的 String^ 参数转换为啥?

】GetFunctionPointerForDelegate将委托中的String^参数转换为啥?【英文标题】:WhatdoesGetFunctionPointerForDelegateconvertaString^parameterinadelegateinto?GetFunctionPointerForDelegate将委托中的String^参数转换为什么?【发布时间】:2011-12-0200:08:19【问题描... 查看详情

为啥我们将 UIViewController 传递给 UINavigationController 类?

】为啥我们将UIViewController传递给UINavigationController类?【英文标题】:WhydowepassaUIViewControllertoaUINavigationControllerClass?为什么我们将UIViewController传递给UINavigationController类?【发布时间】:2012-08-0515:33:37【问题描述】:我对另一个控... 查看详情

为啥 Vuex 状态将数组作为对象返回

】为啥Vuex状态将数组作为对象返回【英文标题】:WhyVuexstatereturnarrayasanobject为什么Vuex状态将数组作为对象返回【发布时间】:2018-11-1802:16:51【问题描述】:我用的是Typescript+vue+vuex,状态定义为conststate:CommonDataState=clients:[],version... 查看详情

为啥 java applet/javafx 没有被广泛使用? (为啥我不应该将它们用于 RIA)[关闭]

】为啥javaapplet/javafx没有被广泛使用?(为啥我不应该将它们用于RIA)[关闭]【英文标题】:Whyjavaapplets/javafxaren\'twidelyused?(whyIshouldn\'tusethemforRIA)[closed]为什么javaapplet/javafx没有被广泛使用?(为什么我不应该将它们用于RIA)[关闭]... 查看详情

为啥 RestTemplate 不将响应表示绑定到 PagedResources?

】为啥RestTemplate不将响应表示绑定到PagedResources?【英文标题】:WhydoesRestTemplatenotbindresponserepresentationtoPagedResources?为什么RestTemplate不将响应表示绑定到PagedResources?【发布时间】:2014-06-0723:26:13【问题描述】:我正在使用spring-da... 查看详情

为啥我无法将文件上传到 Firebase 存储

】为啥我无法将文件上传到Firebase存储【英文标题】:WyhIcannotuploadfiletoFirebaseStorage为什么我无法将文件上传到Firebase存储【发布时间】:2021-12-3109:31:32【问题描述】:我正在尝试将jpg文件上传到Firebase存储。这是我的问题:funshare... 查看详情

为啥不能将匿名方法分配给 var?

】为啥不能将匿名方法分配给var?【英文标题】:Whycan\'tananonymousmethodbeassignedtovar?为什么不能将匿名方法分配给var?【发布时间】:2011-06-2508:00:45【问题描述】:我有以下代码:Func<string,bool>comparer=delegate(stringvalue)returnvalue!=... 查看详情