三目运算符NPE

复合三目运算符问题:
a?b:c?d:e
条件运算符是右结合的,也就是说,从右向左分组计算。例如,a?b:c?d:e 将按
a?b:(c?d:e)执行,从右向左计算,先(c?d:e)再a?b:(c?d:e)。
注意使用过程中,引起的自动拆箱引起的NPE异常:
当第二位和第三位表达式都是包装类型的时候,该表达式的结果才是该包装类型,否则,只要有一个表达式的类型是基本数据类型,则表达式得到的结果都是基本数据类型。如果结果不符合预期,那么编译器就会进行自动拆箱。

boolean flag = true;
 boolean simpleBoolean = false;
 Boolean objectBoolean = Boolean.FALSE;

 // 当第二位和第三位表达式都是对象时,表达式返回值也为对象;
 Boolean x1 = flag ? objectBoolean : objectBoolean;
 // 反编译后代码为:Boolean x1 = flag ? objectBoolean : objectBoolean;
 // 因为 x1 的类型是对象,所以不需要做任何特殊操作。

 // 当第二位和第三位表达式都为基本类型时,表达式返回值也为基本类型;
 boolean x2 = flag ? simpleBoolean : simpleBoolean;
 // 反编译后代码为:boolean x2 = flag ? simpleBoolean : simpleBoolean;
 // 因为 x2 的类型也是基本类型,所以不需要做任何特殊操作。
 // 当第二位和第三位表达式中有一个为基本类型时,表达式返回值也为基本类型;
 boolean x3 = flag ? objectBoolean : simpleBoolean;
 // 反编译后代码为:boolean x3 = flag ? objectBoolean.booleanValue() : simpleBoolean;
 // 因为 x3 的类型是基本类型,所以需要对其中的包装类进行拆箱。

但是,并不是所有人都熟知这个规则,所以在实际应用中,还会出现以下三种定
义方式:

// 当第二位和第三位表达式都是对象时,表达式返回值也为对象;
 boolean x4 = flag ? objectBoolean : objectBoolean;
 // 反编译后代码为:boolean x4 = (flag ? objectBoolean : objectBoolean).
booleanValue();
 // 因为 x4 的类型是基本类型,所以需要对表达式结果进行自动拆箱。

 // 当第二位和第三位表达式都为基本类型时,表达式返回值也为基本类型;
 Boolean x5 = flag ? simpleBoolean : simpleBoolean;
 // 反编译后代码为:Boolean x5 = Boolean.valueOf(flag ? simpleBoolean :
simpleBoolean);
 // 因为 x5 的类型是对象类型,所以需要对表达式结果进行自动装箱。

 // 当第二位和第三位表达式中有一个为基本类型时,表达式返回值也为基本类型;
 Boolean x6 = flag ? objectBoolean : simpleBoolean;
 // 反编译后代码为:Boolean x6 = Boolean.valueOf(flag ? objectBoolean.
booleanValue() : simpleBoolean);
 // 因为 x6 的类型是对象类型,所以需要对表达式结果进行自动装箱。

在开发过程中,如果涉及到三目运算符,那么就要高度注意其中的自动拆装箱问题。

最好的做法就是保持三目运算符的第二位和第三位表达式的类型一致,并且如果
要把三目运算符表达式给变量赋值的时候,也尽量保持变量的类型和他们保持一致。
并且,做好单元测试!!!
如果一定要给出一个方法论去避免这个问题的话,那么在使用的过程中,无论是
三目运算符中的三个表达式,还是三目运算符表达式要赋值的变量,最好都使用包装
类型,可以减少发生错误的概率。

Map<String,Boolean> map = new HashMap<String, Boolean>();
 Boolean b = (map!=null ? map.get(“Hollis”) : false);

以上代码,在小于 JDK 1.8 的版本中执行的结果是 NPE,在 JDK 1.8 及
以后的版本中执行结果是 null。
说下IDK8中
JLS(java规范) 15 中对条件表达式(三目运算符)做了细分之后分为三种,区分方式:
● 如果表达式的第二个和第三个操作数都是布尔表达式,那么该条件表达式就是
布尔表达式
● 如果表达式的第二个和第三个操作数都是数字型表达式,那么该条件表达式就
是数字型表达式
● 除了以上两种以外的表达式就是引用表达式
因为 Boolean b = (map!=null ? map.get(“Hollis”) : false); 表
达式中,第二位操作数为 map.get(“test”),虽然 Map 在定义的时候规定了其值
类型为 Boolean,但是在编译过程中泛型是会被擦除的(泛型的类型擦除),所以,
其结果就是 Object。那么根据以上规则判断,这个表达式就是引用表达式。
又跟据 JLS15.25.3 中规定:
● 如果引用条件表达式出现在赋值上下文或调用上下文中,那么条件表达式就是
合成表达式
因为,Boolean b = (map!=null ? map.get(“Hollis”) : false);
其实就是一个赋值上下文(关于赋值上下文相见 JLS 5.2),所以 map!=null ?
map.get(“Hollis”) : false; 就是合成表达式。
那么 JLS15.25.3 中对合成表达式的操作数类型做了约束:
● 合成的引用条件表达式的类型与其目标类型相同
所以,因为有了这个约束,编译器就可以推断(Java 8 中类型推断,详见 JLS
18)出该表达式的第二个操作数和第三个操作数的结果应该都是 Boolean 类型。
所以,在编译过程中,就可以分别把他们都转成 Boolean 即可,那么以上代码
在 Java 8 中反编译后内容如下:

Boolean b = maps == null ? Boolean.valueOf(false) : (Boolean)maps.
get("Hollis");

但是在 Java 7 中可没有这些规定(Java 8 之前的类型推断功能还很弱),编译
器只知道表达式的第二位和第三位分别是基本类型和包装类型,而无法推断最终表达
式类型。
那么他就会先根据 JLS 15.25 的规定,把返回值结果转换成基本类型。然后在
进行变量赋值的时候,再转换成包装类型:

 Boolean b = Boolean.valueOf(maps == null ? false : ((Boolean)maps.
get("Hollis")).booleanValue());

所以,相比 Java 8 中多了一步自动拆箱,所以会导致 NPE。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值