12

当我像这样编写我的java代码时:

Map<String, Long> map = new HashMap<>()
Long number =null;
if(map == null)
    number = (long) 0;
else
    number = map.get("non-existent key");

该应用程序按预期运行,但是当我这样做时:

Map<String, Long> map = new HashMap<>();
Long number= (map == null) ? (long)0 : map.get("non-existent key");

我在第二行得到一个 NullPointerException。调试指针从第二行跳转到 java.lang.Thread 类中的这个方法:

 /**
     * Dispatch an uncaught exception to the handler. This method is
     * intended to be called only by the JVM.
     */
     private void dispatchUncaughtException(Throwable e) {
         getUncaughtExceptionHandler().uncaughtException(this, e);
     }

这里发生了什么?这两个代码路径是完全等价的,不是吗?


编辑

我正在使用 Java 1.7 U25

4

3 回答 3

15

它们不是等价的。

此表达式的类型

(map == null) ? (long)0 : map.get("non-existent key");

long因为真正的结果有 type long

此表达式具有类型的原因long来自JLS 的第 15.25 节:

如果第二和第三个操作数之一是原始类型T,而另一个的类型是对 应用装箱转换(第 5.1.7 节)的结果T,则条件表达式的类型是T

当您查找不存在的键时,map将返回null. 因此,Java 正试图将其拆箱为long. 但它是null。所以它不能,你得到一个NullPointerException. 您可以通过以下方式解决此问题:

Long number = (map == null) ? (Long)0L : map.get("non-existent key");

然后你会没事的。

然而,在这里,

if(map == null)
    number = (long) 0;
else
    number = map.get("non-existent key");

因为number被声明为Long,所以对 a 的拆箱long永远不会发生。

于 2013-07-09T13:46:37.930 回答
3

这里发生了什么?这两个代码路径是完全等价的,不是吗?

它们不等价;三元运算符有一些注意事项。

三元运算符 的 if-true 参数(long) 0是原始类型long。因此, if-false 参数将自动从Longto拆箱long(根据JLS §15.25):

如果第二个和第三个操作数之一是原始类型T,而另一个的类型是对 应用装箱转换(第 5.1.7 节)的结果T,则条件表达式的类型是T

但是,这个参数是null(因为你的地图不包含字符串"non-existent key",意思是get()返回null),所以NullPointerException在拆箱过程中会发生 a 。

于 2013-07-09T13:44:34.153 回答
0

我在上面评论说他确保map永远不会null,但这对解决三元问题没有帮助。实际上,让系统为您完成工作更容易。他可以使用 Apache Commons Collections 4 及其DefaultedMap类。

import static org.apache.commons.collections4.map.DefaultedMap.defaultedMap;

Map<String, Long> map = ...;  // Ensure not null.
Map<String, Long> dMap = defaultedMap(map, 0L); 

谷歌番石榴没有那么简单的东西,但可以map用这种Maps.transformValues()方法包装。

于 2013-07-09T18:30:12.307 回答