我尝试编写将两位数字转换为2000 +数字的方法,按原样返回所有其他数字,并在将null作为参数传递时返回null。
此实现按预期工作
private Integer convertTo4Digits(Integer modelYear) {
boolean isTwoDigit = modelYear != null && modelYear < 100;
if (isTwoDigit) {
return 2000 + modelYear;
} else {
return modelYear;
}
}
但是当使用NULL调用时,返回语句中的NPE失败了。
private Integer convertTo4Digits(Integer modelYear) {
return (modelYear != null && modelYear < 100) ? (2000 + modelYear) : modelYear;
}
或者这是一个Bug吗?我使用Eclipse Keple和JDK 1.7.0_04
答案 0 :(得分:3)
我认为答案可以在chapter 15.25 of the JLS
中找到如果第二个和第三个操作数之一是原始类型T,而另一个操作数的类型是将装箱转换(第5.1.7节)应用于T的结果,则条件表达式的类型为T. / p>
因此,当第二个或第三个操作数是基本类型时,表达式的类型是基元。因此,如果您传递null
引用,则将执行分支: modelYear
。但由于一个操作数是原始的,因此必须将其取消装箱。这会导致NPE。
如果您查看生成的字节代码
,也可以看到这一点private convertTo4Digits(Ljava/lang/Integer;)Ljava/lang/Integer;
L0
LINENUMBER 46 L0
ALOAD 1
IFNULL L1
ALOAD 1
INVOKEVIRTUAL java/lang/Integer.intValue()I
BIPUSH 100
IF_ICMPGE L1
SIPUSH 2000
ALOAD 1
INVOKEVIRTUAL java/lang/Integer.intValue()I
IADD
GOTO L2
L1
LINENUMBER 47 L1
ALOAD 1
INVOKEVIRTUAL java/lang/Integer.intValue()I
L2
LINENUMBER 46 L2
INVOKESTATIC java/lang/Integer.valueOf(I)Ljava/lang/Integer;
ARETURN
您自己的答案解决了这个问题,因为您将第二个操作数转换为Integer
(modelYear != null && modelYear < 100) ? (Integer) (2000 + modelYear) : modelYear;
因此第二个和第三个操作数都不是原始类型。因此,上面发布的JLS规则不适用,NPE也不见了。
答案 1 :(得分:0)
哦,这个有用(请注意显式转换为Integer):
(modelYear != null && modelYear < 100) ? (Integer) (2000 + modelYear) : modelYear;
问题是:三元运算符的第一个分支确定运算符的结果类型:问题版本中的int
现在,modelYear(null)被取消装箱,导致NPE再次被装箱之前就已经装箱了。