为什么Java编译器有时允许取消对null的装箱?

为什么Java编译器有时允许取消对null的装箱?,java,compilation,javac,unboxing,Java,Compilation,Javac,Unboxing,例如: int anInt = null; 编译时失败,但 public static void main(String[] args) { for (int i = 0; i < 10; i++) { System.out.println("" + getSomeVal()); } } public static int getSomeVal() { return new Random().nextBoolean() ? 1 : null; } publicst

例如:

int anInt = null;
编译时失败,但

public static void main(String[] args) {
  for (int i = 0; i < 10; i++) {
    System.out.println("" + getSomeVal());
  }
}
public static int getSomeVal() {
   return new Random().nextBoolean() ? 1 : null;
}
publicstaticvoidmain(字符串[]args){
对于(int i=0;i<10;i++){
System.out.println(“+getSomeVal());
}
}
公共静态int getSomeVal(){
返回new Random().nextBoolean()?1:null;
}

在运行时失败(通常)。尝试仅返回
null
也会导致编译错误,因此我假设有多个路径会导致编译器推断
null
可能是自动装箱的
int
?为什么javac不能用相同的错误编译这两种情况?

在第一种情况下,编译器知道您正在尝试取消编译时常量
null
的装箱

在第二种情况下,条件表达式的类型是
Integer
,因此您实际上在编写:

Integer tmp = new Random().nextBoolean() ? 1 : null;
return (int) tmp;
。。。因此,取消装箱不会发生在常量表达式上,编译器将允许它

如果通过取消装箱将其更改为强制条件表达式的类型
int
,它将失败:

// Compile-time failure
return new Random().nextBoolean() ? 1 : (int) null;

装箱部分隐藏了原语和相应的包装器对象之间的区别,但不会删除它

有两个区别不会因装箱而改变:

  • 对象可以为null,而基本体不能为null
  • 对象既有状态又有标识,而基本体只有状态(值)
有时,这些差异会在使用装箱时引起问题

要记住的几点:

  • 注意空值。自动取消绑定空对象将导致
    NullPointerException
  • =
    相等的项目进行比较时必须小心
    
不能将null赋值给int

    int anInt = null;
    System.out.println("" + getSomeVal()); //null was just converted to a srting and was printed
Java允许这样做,因为您没有将null赋值给int

    int anInt = null;
    System.out.println("" + getSomeVal()); //null was just converted to a srting and was printed
如果执行此操作,则可能会出现错误

    int anInt = getSomeVal();

第一种情况涉及将null“type”隐式转换为int类型,从而导致编译器错误。第二种情况隐式地将null强制转换为左侧变量类型,该类型是表达式的类型,而不是返回值。我猜仔细阅读JLS 15.25“如果第二个和第三个操作数具有相同的类型(可能是null类型),那么这就是条件表达式的类型。”可以得出这一点,因为java编译器无法为null指定int类型,因此必须通过取消绑定1来为这两个操作数使用整数。回答很清楚,谢谢。