从null转换为int可能吗?

我知道,当我读到这个答案的时候,我会看到我忽略了一些我眼中的东西。 但我花了最后的30分钟试图弄清楚自己没有结果。

所以,我正在用Java 6编写一个程序,发现了一些(对我来说)奇怪的特性。 为了试图隔离它,我做了两个小例子。 我首先尝试了以下方法:

private static int foo() { return null; } 

编译器拒绝它:types不匹配:不能从null转换为int。

这对我很好,它尊重我熟悉的Java语义。 然后我尝试了以下内容:

 private static Integer foo(int x) { if (x < 0) { return null; } else { return new Integer(x); } } private static int bar(int x) { Integer y = foo(x); return y == null ? null : y.intValue(); } private static void runTest() { for (int index = 2; index > -2; index--) { System.out.println("bar(" + index + ") = " + bar(index)); } } 

这编译没有错误! 但是,在我看来,应该有一个types转换错误的线

  return y == null ? null : y.intValue(); 

如果我运行该程序,我会得到以下输出:

 bar(2) = 2 bar(1) = 1 bar(0) = 0 Exception in thread "main" java.lang.NullPointerException at Test.bar(Test.java:23) at Test.runTest(Test.java:30) at Test.main(Test.java:36) 

你能解释这种行为吗?

更新

非常感谢你提供了很多明确的答案。 我有点担心,因为这个例子并不符合我的直觉。 有一件事让我感到不安,那就是一个null被转换为一个int,我想知道结果是什么:0就像在C ++中一样? 这将是非常奇怪的。 很好,转换在运行时是不可能的(空指针exception)。

让我们看看这一行:

 return y == null ? null : y.intValue(); 

? : ? :声明,双方:必须具有相同的types。 在这种情况下,Java将使其具有Integertypes。 Integer可以为null ,所以左侧是可以的。 expression式y.intValue()的types是int ,但是Java会自动把它整理为Integer (注意,你也可以写y来保存这个自动复制)。

现在,结果必须重新解包为int ,因为方法的返回types是int 。 如果您取消装入一个为nullInteger ,您将得到一个NullPointerException

注意:Java语言规范第15.25节解释了关于types转换的确切规则? : ? :条件运算符。

番石榴有一个非常优雅的解决scheme,使用更多的MoreObjects.firstNonNull

 Integer someNullInt = null; int myInt = MoreObjects.firstNonNull(someNullInt, 0); 

返回types的types由Java在这里推断。 这是问题..

http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.25

这是实际的问题 –

如果第二个和第三个操作数中的一个是空types,而另一个的types是引用types,那么条件expression式的types就是该引用types。

所以,编译器基本上推断条件expression式的返回types是Integer,这就是为什么它允许你成功编译。

编辑:请参阅评论中的规则

这说明了人类读取代码和编译器读取代码的方式之间的问题区别。

看到一个三元expression式的时候,你很可能用if / else语句把它分成两部分:

 if (y == null) return null; else return y.intValue(); 

可以看到这是无效的,因为它导致一个可能的分支,一个定义为返回一个int方法实际上返回null (非法!)。

编译器看到的是一个expression式 ,它必须有一个types。 它指出,三元操作包括一方null ,另一方为int 。 由于Java的自动装箱行为,它会提出一个“最好的猜测”(我的术语,而不是Java的),expression式的types是什么: Integer (这是公平的:这是唯一的合法的types可以为null或盒装的int )。

由于该方法应该返回一个int ,所以从编译器的angular度来看,这是没问题的:返回的expression式是一个Integer ,它可以被自动取消装箱。

以防万一你的项目中没有Guava,但已经使用Apache Commons,你可以使用Apache Lang3和它的ObjectUtils类。

用法与番石榴基本相同:

 Integer number = null; int notNull = ObjectUtils.firstNonNull(number, 0); 

请注意,Guava库中的这个方法比Apache更快。 下面是我刚刚在笔记本电脑(酷睿i7-7500U 2.7 GHz),Oracle Java 8,多次运行,JVM预热的结果进行了简短的比较:

 ╔══════════════╦══════╦══════╦════════╦══════╗ ║ Library/Runs ║ 1000 ║ 1mln ║ 100mln ║ 1bln ║ ╠══════════════╬══════╬══════╬════════╬══════╣ ║ Apache ║ 1 ║ 30 ║ 782 ║ 9981 ║ ║ Guava ║ 1 ║ 22 ║ 120 ║ 828 ║ ╚══════════════╩══════╩══════╩════════╩══════╝ 

结果以毫秒为单位。 我不认为你经常需要运行这个方法数十亿次,但是仍然有比较好的performance

autounboxing null值的问题可能真的很烦人。 在你的例子中,它是三元运算符结果types推断和自动装箱的组合(JLS应该被咨询为什么它的行为如此)

但一般来说,你应该尽量避免使用包装types。 使用int而不是Integer 。 如果你需要一个特殊的值,意思是“没有结果”,那么你可以使用Integer.MAX_VALUE作为例子。

这个编译

 private static int foo() { return (Integer)null; }