I know that when I read the answer to this I will see that I have overlooked something that was under my eyes. But I have spent the last 30 minutes trying to figure it out myself with no result.
我知道当我读到这个答案的时候,我就会发现我忽略了我眼前的一些东西。但我花了30分钟的时间试图自己弄清楚,但没有结果。
So, I was writing a program in Java 6 and discovered some (for me) strange feature. In order to try and isolate it, I have made two small examples. I first tried the following method:
因此,我在Java 6中编写了一个程序,并发现了一些(对我来说)奇怪的特性。为了尝试分离它,我做了两个小的例子。我首先尝试了以下方法:
private static int foo()
{
return null;
}
and the compiler refused it: Type mismatch: cannot convert from null to int.
而编译器拒绝它:类型不匹配:不能从null转换为int。
This is fine with me and it respects the Java semantics I am familiar with. Then I tried the following:
这对我来说很好,它尊重我所熟悉的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));
}
}
This compiles with no errors! But, in my opinion, there should be a type conversion error in the line
这个编译没有错误!但是,在我看来,应该有一个类型转换错误。
return y == null ? null : y.intValue();
If I run the program I get the following output:
如果我运行程序,我得到如下输出:
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)
Can you explain this behaviour?
你能解释一下这种行为吗?
Update
更新
Thank you very much for the many clarifying answers. I was a bit worried because this example did not correspond to my intuition. One thing that disturbed me was that a null was being converted to an int and I was wondering what the result would be: 0 like in C++? That would hae been very strange. Good that the conversion is not possible at runtime (null pointer exception).
非常感谢你的解答。我有点担心,因为这个例子不符合我的直觉。有一件事让我感到不安,那就是一个null被转换成int,我想知道结果会是什么?那就太奇怪了。很好,在运行时不可能转换(空指针异常)。
7 个解决方案
#1
55
Let's look at the line:
让我们看看这条线:
return y == null ? null : y.intValue();
In a ? :
statement, both sides of the :
must have the same type. In this case, Java is going to make it have the type Integer
. An Integer
can be null
, so the left side is ok. The expression y.intValue()
is of type int
, but Java is going to auto-box this to Integer
(note, you could just as well have written y
which would have saved you this autobox).
在一个?:声明,双方的:必须有相同的类型。在这种情况下,Java将使它具有类型整数。一个整数可以是空的,所以左边是可以的。这个表达式。intvalue()是int类型的,但是Java会自动将其设置为整数(注意,你也可以写y,这样就可以为你保存这个autobox)。
Now, the result has to be unboxed again to int
, because the return type of the method is int
. If you unbox an Integer
that is null
, you get a NullPointerException
.
现在,结果必须再次被解压到int,因为方法的返回类型是int类型。如果你打开一个空的整数,你会得到一个NullPointerException。
Note: Paragraph 15.25 of the Java Language Specification explains the exact rules for type conversions with regard to the ? :
conditional operator.
注意:Java语言规范的第15.25段解释了关于类型转换的确切规则。:条件操作符。
#2
12
Guava has a pretty elegant solution for this using MoreObjects.firstNonNull
:
Guava有一个非常优雅的解决方案,它使用了更多的对象。firstnonnull:
Integer someNullInt = null;
int myInt = MoreObjects.firstNonNull(someNullInt, 0);
#3
6
The type of the return type is inferred by Java here. That is the issue ..
返回类型的类型在这里由Java推断出来。这就是问题所在。
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.25
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html 15.25
Here is the actual problem --
这是实际问题。
If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.
如果第二个和第三个操作数中的一个是null类型,另一个是引用类型,那么条件表达式的类型就是引用类型。
So, basically the compiler infers the return type of the conditional expression to be Integer and thats why it allows you to compile successfully.
所以,基本上编译器会推断出条件表达式的返回类型为整数,这就是为什么它允许你成功编译的原因。
EDIT : See rules in comments
编辑:参见注释中的规则。
#4
3
This illustrates a problematic difference between the way a human reads code and a compiler reads code.
这说明了人类阅读代码和编译器读取代码之间存在问题的不同之处。
When you see a ternary expression, it's very possible for you to mentally split it into two parts, in the style of an if
/else
statement:
当你看到一个三元表达式时,你很有可能把它分成两部分,用if/else语句的形式:
if (y == null)
return null;
else
return y.intValue();
You can see that this is invalid, as it results in a possible branch where a method defined to return an int
is actually returning null
(illegal!).
您可以看到这是无效的,因为它会导致一个可能的分支,其中定义的返回int的方法实际上是返回null(非法的!)
What the compiler sees is an expression, which must have a type. It notes that the ternary operation includes a null
on one side and an int
on the other; due to Java's autoboxing behavior, it then comes up with a "best guess" (my term, not Java's) as to what the expression's type is: Integer
(this is fair: it's the only type which could legally be null
or a boxed int
).
编译器看到的是一个表达式,它必须有一个类型。它注意到三元运算包括一边的空值和另一边的int数;由于Java的自动装箱行为,它会产生一个“最佳猜测”(我的术语,而不是Java的),至于表达式的类型是什么:Integer(这是公平的:它是唯一可以合法地为null或boxed int的类型)。
Since the method is supposed to return an int
, this is fine from the compiler's perspective: the expression being returned evaluates to an Integer
, which can be unboxed automatically.
由于该方法应该返回一个int类型,所以从编译器的角度来看,这是可以的:返回的表达式计算为整数,可以自动解压。
#5
3
Just in case you don't have Guava in your project, but already using Apache Commons, you might utilize Apache Lang3 with its ObjectUtils class.
如果您的项目中没有Guava,但是已经使用了Apache Commons,您可能会使用Apache Lang3和它的ObjectUtils类。
The usage is basically the same as Guava:
其用法基本与番石榴相同:
Integer number = null;
int notNull = ObjectUtils.firstNonNull(number, 0);
Note, that this method in Guava library works faster, than in Apache. Here is a short comparison I just made on my laptop (Core i7-7500U 2.7 GHz), Oracle Java 8, multiple runs, JVM preheated, results are averaged:
注意,在Guava库中这种方法比在Apache中工作得更快。下面是我在笔记本电脑上做的一个简短的对比(Core 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 ║
╚══════════════╩══════╩══════╩════════╩══════╝
Results are in milliseconds. I don't think you often need to run this method for billions of times, but still, it is always good to have performance comparison
结果是,以毫秒为单位。我不认为您经常需要运行这个方法几十亿次,但是仍然,有性能比较总是好的。
#6
1
The problem with autounboxing null
values can be really annoying. In your example it's a combination of ternary operator result type inferring and autounboxing (the JLS should be consulted why it behaves like that)
autounboxing null值的问题可能非常恼人。在您的示例中,它是三元运算符结果类型推断和自定义的组合(应该咨询JLS的行为方式)
But generally, you should try to avoid using wrapper types. Use int
instead of Integer
. If you need a special value meaning "no result", then you can use Integer.MAX_VALUE
for example.
但是通常,您应该尽量避免使用包装器类型。使用int而不是Integer。如果您需要一个特殊的值,意思是“没有结果”,那么您可以使用整数。MAX_VALUE为例。
#7
1
this compiles
这个编译
private static int foo()
{
return (Integer)null;
}
#1
55
Let's look at the line:
让我们看看这条线:
return y == null ? null : y.intValue();
In a ? :
statement, both sides of the :
must have the same type. In this case, Java is going to make it have the type Integer
. An Integer
can be null
, so the left side is ok. The expression y.intValue()
is of type int
, but Java is going to auto-box this to Integer
(note, you could just as well have written y
which would have saved you this autobox).
在一个?:声明,双方的:必须有相同的类型。在这种情况下,Java将使它具有类型整数。一个整数可以是空的,所以左边是可以的。这个表达式。intvalue()是int类型的,但是Java会自动将其设置为整数(注意,你也可以写y,这样就可以为你保存这个autobox)。
Now, the result has to be unboxed again to int
, because the return type of the method is int
. If you unbox an Integer
that is null
, you get a NullPointerException
.
现在,结果必须再次被解压到int,因为方法的返回类型是int类型。如果你打开一个空的整数,你会得到一个NullPointerException。
Note: Paragraph 15.25 of the Java Language Specification explains the exact rules for type conversions with regard to the ? :
conditional operator.
注意:Java语言规范的第15.25段解释了关于类型转换的确切规则。:条件操作符。
#2
12
Guava has a pretty elegant solution for this using MoreObjects.firstNonNull
:
Guava有一个非常优雅的解决方案,它使用了更多的对象。firstnonnull:
Integer someNullInt = null;
int myInt = MoreObjects.firstNonNull(someNullInt, 0);
#3
6
The type of the return type is inferred by Java here. That is the issue ..
返回类型的类型在这里由Java推断出来。这就是问题所在。
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.25
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html 15.25
Here is the actual problem --
这是实际问题。
If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.
如果第二个和第三个操作数中的一个是null类型,另一个是引用类型,那么条件表达式的类型就是引用类型。
So, basically the compiler infers the return type of the conditional expression to be Integer and thats why it allows you to compile successfully.
所以,基本上编译器会推断出条件表达式的返回类型为整数,这就是为什么它允许你成功编译的原因。
EDIT : See rules in comments
编辑:参见注释中的规则。
#4
3
This illustrates a problematic difference between the way a human reads code and a compiler reads code.
这说明了人类阅读代码和编译器读取代码之间存在问题的不同之处。
When you see a ternary expression, it's very possible for you to mentally split it into two parts, in the style of an if
/else
statement:
当你看到一个三元表达式时,你很有可能把它分成两部分,用if/else语句的形式:
if (y == null)
return null;
else
return y.intValue();
You can see that this is invalid, as it results in a possible branch where a method defined to return an int
is actually returning null
(illegal!).
您可以看到这是无效的,因为它会导致一个可能的分支,其中定义的返回int的方法实际上是返回null(非法的!)
What the compiler sees is an expression, which must have a type. It notes that the ternary operation includes a null
on one side and an int
on the other; due to Java's autoboxing behavior, it then comes up with a "best guess" (my term, not Java's) as to what the expression's type is: Integer
(this is fair: it's the only type which could legally be null
or a boxed int
).
编译器看到的是一个表达式,它必须有一个类型。它注意到三元运算包括一边的空值和另一边的int数;由于Java的自动装箱行为,它会产生一个“最佳猜测”(我的术语,而不是Java的),至于表达式的类型是什么:Integer(这是公平的:它是唯一可以合法地为null或boxed int的类型)。
Since the method is supposed to return an int
, this is fine from the compiler's perspective: the expression being returned evaluates to an Integer
, which can be unboxed automatically.
由于该方法应该返回一个int类型,所以从编译器的角度来看,这是可以的:返回的表达式计算为整数,可以自动解压。
#5
3
Just in case you don't have Guava in your project, but already using Apache Commons, you might utilize Apache Lang3 with its ObjectUtils class.
如果您的项目中没有Guava,但是已经使用了Apache Commons,您可能会使用Apache Lang3和它的ObjectUtils类。
The usage is basically the same as Guava:
其用法基本与番石榴相同:
Integer number = null;
int notNull = ObjectUtils.firstNonNull(number, 0);
Note, that this method in Guava library works faster, than in Apache. Here is a short comparison I just made on my laptop (Core i7-7500U 2.7 GHz), Oracle Java 8, multiple runs, JVM preheated, results are averaged:
注意,在Guava库中这种方法比在Apache中工作得更快。下面是我在笔记本电脑上做的一个简短的对比(Core 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 ║
╚══════════════╩══════╩══════╩════════╩══════╝
Results are in milliseconds. I don't think you often need to run this method for billions of times, but still, it is always good to have performance comparison
结果是,以毫秒为单位。我不认为您经常需要运行这个方法几十亿次,但是仍然,有性能比较总是好的。
#6
1
The problem with autounboxing null
values can be really annoying. In your example it's a combination of ternary operator result type inferring and autounboxing (the JLS should be consulted why it behaves like that)
autounboxing null值的问题可能非常恼人。在您的示例中,它是三元运算符结果类型推断和自定义的组合(应该咨询JLS的行为方式)
But generally, you should try to avoid using wrapper types. Use int
instead of Integer
. If you need a special value meaning "no result", then you can use Integer.MAX_VALUE
for example.
但是通常,您应该尽量避免使用包装器类型。使用int而不是Integer。如果您需要一个特殊的值,意思是“没有结果”,那么您可以使用整数。MAX_VALUE为例。
#7
1
this compiles
这个编译
private static int foo()
{
return (Integer)null;
}