前两天在开发中遇到了一个很诡异的NPE情况,最后发现是三目运算符的编译问题,这里记录一下。
简化后的代码如下:
Map<String, Long> map = Maps.newHashMap();
map.put("a", 123L);
TmpCls cls = new TmpCls();
cls.setPrice(ObjectUtils.isEmpty(map) ? 0L : map.get("b"));
原代码的map其实是从数据库查出的结果,如果没查到数据,price就是0,否则就取查到的数据,有时就会出现上面例子的情况,map.get()取不到数据。
从代码本身来看,是没任何问题的,但是遇到这种取不到的情况,就会报一个NPE,百思不得其解。最后看了一下反编译的结果,才发现其中原因。
反编译以后,最后的那个三目运算符的代码是这样的
cls.setPrice(ObjectUtils.isEmpty((Object)map) ? 0L : ((long)map.get("b"));
会发现,它会将map.get()方法外包进行了long型强转,如果这里没有获取到信息,那么就是null了。
具体原因还真不清楚,大概猜想,应该是编译器根据目标类型进行了推导的结果。
解决的办法有三种:
1、不使用三目运算符,这个比较简单,就不说明了
2、对原数据进行判空操作,可以使用Optional进行简单包装。
cls.setPrice(ObjectUtils.isEmpty(map) ? 0L : Optional.ofNullable(map.get("b")).orElse(0L));
3、将后面的逻辑移到外面先做处理,不过这里也有一个小问题。
long l=ObjectUtils.isEmpty(map) ? 0L : map.get("b");//NPE
Long l=ObjectUtils.isEmpty(map) ? 0L : map.get("b");//正常
我也是通过这个测试,才推断应该是编译器进行了类型推导的。
另外还需要说一下,我用eclipse和idea都进行了这个测试,发现在eclipse中,第三种解决办法里,即使使用包装类型,也依然会有NPE产生,这个应该是IDE的问题吧,因为我查了一下用的都是自己安装的JDK8。