有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java NullPointerException从何而来?

我试图编写一个方法,将一个两位数的数字转换为2000+的数字,返回所有其他数字的原样,并在将null作为参数传递时返回null

此实现按预期工作

private Integer convertTo4Digits(Integer modelYear) {
    boolean isTwoDigit = modelYear != null && modelYear < 100;
    if (isTwoDigit) {
        return 2000 + modelYear;
    } else {
        return modelYear;
    }
}

但是,当使用NULL调用时,这个函数在return语句中使用NPE失败

private Integer convertTo4Digits(Integer modelYear) {
    return (modelYear != null && modelYear < 100) ? (2000 + modelYear) : modelYear;
}

还是这是一只虫子?我将EclipseKeple与JDK1.7.0\u04一起使用


共 (2) 个答案

  1. # 1 楼答案

    哦,该死,这一个有效(注意显式转换为整数):

     (modelYear != null && modelYear < 100) ? (Integer) (2000 + modelYear) : modelYear;
    

    问题是:三元运算符的第一个分支决定了问题版本中运算符的结果类型:int

    现在modelYear(null)被取消装箱,这会导致NPE在再次装箱之前就被取消装箱

  2. # 2 楼答案

    我认为答案可以在chapter 15.25 of the JLS中找到

    If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.

    因此,当第二个或第三个操作数是基元类型时,表达式的类型是基元类型。因此,如果传递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也不复存在