NullPointerException来自哪里?

时间:2014-03-06 07:30:17

标签: java nullpointerexception ternary-operator

我尝试编写将两位数字转换为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

2 个答案:

答案 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再次被装箱之前就已经装箱了。