Boolean.valueOf()有时会产生NullPointerException


115

我有以下代码:

package tests;

import java.util.Hashtable;

public class Tests {

    public static void main(String[] args) {

        Hashtable<String, Boolean> modifiedItems = new Hashtable<String, Boolean>();

        System.out.println("TEST 1");
        System.out.println(modifiedItems.get("item1")); // Prints null
        System.out.println("TEST 2");
        System.out.println(modifiedItems.get("item1") == null); // Prints true
        System.out.println("TEST 3");
        System.out.println(Boolean.valueOf(null)); // Prints false
        System.out.println("TEST 4");
        System.out.println(Boolean.valueOf(modifiedItems.get("item1"))); // Produces NullPointerException
        System.out.println("FINISHED!"); // Never executed
    }
}

我的问题是我不明白为什么Test 3可以正常工作(它打印false并且不产生NullPointerException),而Test 4却抛出了NullPointerException。正如你可以看到测试12nullmodifiedItems.get("item1")是平等的和null

Java 7和8中的行为相同。


modifiedItems.get(“ item1”)这是null,您知道这一点,但是您假设将其传递给valueOf不会以NPE结尾吗?
Stultuske '17

16
@Stultuske:这是一个有效的问题,因为仅在将文字传递null给同一函数的上方两行不会生成NPE!这样做有充分的理由,但是乍一看肯定会令人困惑:-)
psmears '17

25
我印象深刻。这是我多年来见过的最有趣的空指针异常问题。
candied_orange

@杰罗恩这不是这个问题的重复。拆箱确实是这两个问题的共同点,但这里没有进行比较。这个问题的关键在于它的发生是由于解决过载的方式。这与应用方式完全不同==
安迪·特纳

Answers:


178

您必须仔细查看正在调用的重载:

  • Boolean.valueOf(null)正在调用Boolean.valueOf(String)NPE即使提供了null参数,也不会抛出“ even”。
  • Boolean.valueOf(modifiedItems.get("item1"))正在调用Boolean.valueOf(boolean),因为modifiedItems的值类型为Boolean,需要进行拆箱转换。既然modifiedItems.get("item1")null,那就是该值的拆箱-而不是Boolean.valueOf(...)-引发NPE。

确定调用哪个重载的规则非常繁琐,但它们大致如下:

  • 在第一遍中,搜索方法匹配项而不允许装箱/拆箱(也不使用可变Arity方法)。

    • 因为null为一个可接受的值String,但不booleanBoolean.valueOf(null)被匹配到Boolean.valueOf(String)在此通;
    • BooleanBoolean.valueOf(String)或不可接受的Boolean.valueOf(boolean),因此在此传递中没有方法匹配Boolean.valueOf(modifiedItems.get("item1"))
  • 在第二遍中,搜索方法匹配项,以允许装箱/拆箱(但仍然不是可变arity方法)。

    • Boolean可以将A 拆箱boolean,因此在此过程中Boolean.valueOf(boolean)与匹配Boolean.valueOf(modifiedItems.get("item1"));但是编译器必须插入拆箱转换才能调用它:Boolean.valueOf(modifiedItems.get("item1").booleanValue())
  • (第三遍允许使用可变Arity方法,但此处不相关,因为前两个遍历与这些情况匹配)


3
如果使用Boolean.valueOf(modifiedItems.get("item1").booleanValue())源代码而不是源代码,代码会更清晰Boolean.valueOf(modifiedItems.get("item1"))吗?
CauseUnderflowsEverywhere'Oct

1
@CausingUnderflowsEverywhere不是真的-真的很难看到它.booleanValue()埋在表达式中。有两个发现:1)自动装箱是Java的一项有意功能,可消除语法残障;自己做是可能的,但不是惯用的;2)这根本没有帮助您-它当然不会阻止问题的发生,也不会在发生故障时提供任何额外的信息(堆栈跟踪将是相同的,因为执行的代码是相同的)。
安迪·特纳

@CausingUnderflows在任何地方最好使用工具突出显示问题,例如intellij会在这里为您带来潜在的NPE。
安迪·特纳

13

由于modifiedItems.get返回a Boolean不可转换为String),因此将使用的签名为Boolean.valueOf(boolean),其中相Boolean对于Primitive开箱boolean。一旦null返回那里,发件箱就会失败,并显示NullPointerException


11

方法签名

该方法Boolean.valueOf(...)具有两个签名:

  1. public static Boolean valueOf(boolean b)
  2. public static Boolean valueOf(String s)

您的modifiedItems价值是Boolean。您不能强制Boolean转换String,因此将选择第一个签名

布尔拆箱

在你的陈述中

Boolean.valueOf(modifiedItems.get("item1"))

可以读成

Boolean.valueOf(modifiedItems.get("item1").booleanValue())   

但是,modifiedItems.get("item1")返回值可以null使您基本上得到

null.booleanValue()

显然导致 NullPointerException


措词不正确,感谢您的指点和回答,将根据您的反馈进行更新。抱歉,我在撰写本文时没有看到您的答案,我发现我的情况看起来像您。我应该删除答案以避免对OP感到困惑吗?
铝未

4
不要将其删除。请记住,这不是一个零和游戏:人们可以(并且确实)赞成多个答案。
安迪·特纳

3

正如Andy已经很好地描述了以下原因NullPointerException

这是由于布尔拆箱:

Boolean.valueOf(modifiedItems.get("item1"))

转换为:

Boolean.valueOf(modifiedItems.get("item1").booleanValue())

在运行时,然后抛出NullPointerException是否modifiedItems.get("item1")为null。

现在,我想在这里再说一点,如果以下类对它们各自的原语进行NullPointerException拆箱,如果它们对应的返回对象为null,也会产生异常。

  1. 字节-字节
  2. 字符-字符
  3. 浮动-浮动
  4. int-整数
  5. 长-长
  6. 短-短
  7. 双-双

这是代码:

    Hashtable<String, Boolean> modifiedItems1 = new Hashtable<String, Boolean>();
    System.out.println(Boolean.valueOf(modifiedItems1.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Byte> modifiedItems2 = new Hashtable<String, Byte>();
    System.out.println(Byte.valueOf(modifiedItems2.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Character> modifiedItems3 = new Hashtable<String, Character>();
    System.out.println(Character.valueOf(modifiedItems3.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Float> modifiedItems4 = new Hashtable<String, Float>();
    System.out.println(Float.valueOf(modifiedItems4.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Integer> modifiedItems5 = new Hashtable<String, Integer>();
    System.out.println(Integer.valueOf(modifiedItems5.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Long> modifiedItems6 = new Hashtable<String, Long>();
    System.out.println(Long.valueOf(modifiedItems6.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Short> modifiedItems7 = new Hashtable<String, Short>();
    System.out.println(Short.valueOf(modifiedItems7.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Double> modifiedItems8 = new Hashtable<String, Double>();
    System.out.println(Double.valueOf(modifiedItems8.get("item1")));//Exception in thread "main" java.lang.NullPointerException

1
“在运行时转换为...”将在编译时转换为。
安迪·特纳

0

理解它的一种方法是何时Boolean.valueOf(null)调用,正是被告知java评估null。

但是,当Boolean.valueOf(modifiedItems.get("item1"))调用Java 时,它被告知要从对象类型为Boolean的HashTable中获取一个值,但是它没有找到布尔类型,而是找到了一个死角(空值),即使它期望布尔值也是如此。抛出NullPointerException异常是因为这部分Java的创建者认为,这种情况是程序中发生错误的实例,需要程序员注意。(发生了意外的事情。)

在这种情况下,故意声明您打算在其中放置null与Java查找对要在其中找到对象的对象(null)的缺失引用之间的区别更大。

在此答案中查看有关NullPointerException的更多信息:https : //stackoverflow.com/a/25721181/4425643


如果有人可以帮助改善这个答案,我想到的是一个词,指的是程序员明确地写东西,没有歧义
CausingUnderflowsEverywhere
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.