从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将使其具有Integer
types。 Integer
可以为null
,所以左侧是可以的。 expression式y.intValue()
的types是int
,但是Java会自动把它整理为Integer
(注意,你也可以写y
来保存这个自动复制)。
现在,结果必须重新解包为int
,因为方法的返回types是int
。 如果您取消装入一个为null
的Integer
,您将得到一个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; }