可以从null转换为int吗?

时间:2011-07-05 21:05:09

标签: java

我知道当我读到这个答案时,我会发现我忽略了一些东西 那是在我眼前。但我花了最后30分钟试图弄明白自己 没有结果。

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

private static int foo()
{
    return null;
}

并且编译器拒绝它:类型不匹配:无法从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));
    }
}

这个编译没有错误!但是,在我看来,应该存在类型转换错误 在行

    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 ++中?那会很奇怪。 很好,在运行时无法进行转换(空指针异常)。

6 个答案:

答案 0 :(得分:59)

让我们来看看这一行:

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

? :声明中,:的两边必须具有相同的类型。在这种情况下,Java将使其具有类型IntegerInteger可以null,因此左侧可以。表达式y.intValue()的类型为int,但Java会自动将其设置为Integer(请注意,您也可以编写y来保存你这个autobox)。

现在,结果必须再次取消装箱到int,因为方法的返回类型为int。如果您取消Integer null NullPointerException,则会获得? :

注意:Java语言规范的Paragraph 15.25解释了与{{1}}条件运算符相关的类型转换的确切规则。

答案 1 :(得分:17)

Guava使用MoreObjects.firstNonNull提供了非常优雅的解决方案:

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

答案 2 :(得分:6)

返回类型的类型由Java在这里推断。这就是问题..

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

这是实际问题 -

如果第二个和第三个操作数之一是null类型而另一个操作数的类型是引用类型,则条件表达式的类型是该引用类型。

因此,基本上编译器会将条件表达式的返回类型推断为Integer,这就是为什么它允许您成功编译。

编辑:参见评论中的规则

答案 3 :(得分:5)

如果您的项目中没有Guava,但已经使用了Apache Commons,您可以将Apache Lang3ObjectUtils类一起使用。

用法与Guava基本相同:

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

请注意,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 ║
╚══════════════╩══════╩══════╩════════╩══════╝

结果以毫秒为单位。 我不认为你经常需要运行这种方法数十亿次,但是,进行性能比较总是好的

答案 4 :(得分:3)

这说明 human 读取代码和编译器读取代码的方式之间存在问题。

看到一个三元表达式时,你很可能会以if / else语句的风格将它精神上分成两部分:

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

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

编译器看到的是表达式,它必须具有类型。它指出,三元操作一方包括null,另一方包括int;由于Java的自动装箱行为,它然后提出了关于表达式类型是什么的“最佳猜测”(我的术语,而不是Java):Integer(这是公平的:它是唯一合法的null {1}}或装箱int)。

由于该方法应该返回int,从编译器的角度来看这很好:返回的表达式求值为Integer,可以自动取消装箱。

答案 5 :(得分:1)

自动装箱null值的问题可能非常烦人。在您的示例中,它是三元运算符结果类型推断和自动装箱的组合(应该查询JLS,为什么它的行为类似)

但一般来说,你应该尽量避免使用包装类型。使用int代替Integer。如果您需要一个特殊值,意味着“没有结果”,那么您可以使用Integer.MAX_VALUE作为例子。