Boolean.valueOf() иногда создает исключение NullPointerException

У меня есть этот код:

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), между тем тест 4 бросает a NullPointerException. Как вы можете видеть в тестах 1 и 2, null и modifiedItems.get("item1") равны и null.

Поведение одинаково в Java 7 и 8.

Ответ 1

Вам нужно внимательно посмотреть, на что вызывается перегрузка:

  • Boolean.valueOf(null) вызывает Boolean.valueOf(String). Это не бросает NPE, даже если поставляется с нулевым параметром.
  • Boolean.valueOf(modifiedItems.get("item1")) вызывает Boolean.valueOf(boolean), потому что значения modifiedItems имеют тип Boolean, для которого требуется преобразование unboxing. Так как modifiedItems.get("item1") null, то это unboxing этого значения, а не Boolean.valueOf(...), который выбрасывает NPE.

Правила определения того, какая перегрузка вызывается, довольно волосатые, но они примерно идут следующим образом:

  • В первом проходе выполняется поиск совпадения методов без разрешения метода бокса/распаковки (или методов переменной arity).

    • Поскольку null является допустимым значением для String, но не Boolean, Boolean.valueOf(null) соответствует этому значению Boolean.valueOf(String);
    • Boolean не является приемлемым ни для Boolean.valueOf(String), ни для Boolean.valueOf(boolean), поэтому в этом проходе для Boolean.valueOf(modifiedItems.get("item1")) не указан ни один метод.
  • Во втором проходе выполняется поиск метода, позволяющий использовать методы бокса/распаковки (но все же не переменные arity).

    • A Boolean может быть распакован на Boolean, поэтому Boolean.valueOf(boolean) соответствует этому значению Boolean.valueOf(modifiedItems.get("item1")); но компилятор должен вставлять преобразование unboxing для его вызова: Boolean.valueOf(modifiedItems.get("item1").booleanValue())
  • (Там третий проход, позволяющий использовать методы переменной arity, но который здесь не уместен, поскольку первые два прохода соответствуют этим случаям)

Ответ 2

Так как modifiedItems.get возвращает Boolean (который не применим к String), сигнатура, которая будет использоваться, Boolean.valueOf(boolean), где Boolean переименовывается в примитив Boolean. Как только null возвращается туда, сбой при исходящей передаче с NullPointerException.

Ответ 3

Подпись метода

Метод Boolean.valueOf(...) имеет две подписи:

  • public static Boolean valueOf(boolean b)
  • 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()

что, очевидно, приводит к a NullPointerException

Ответ 4

Поскольку Энди уже очень хорошо описал причину NullPointerException:

что связано с булевым un-боксом:

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

преобразуется в:

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

во время выполнения, а затем он бросает NullPointerException, если modifiedItems.get("item1") имеет значение null.

Теперь я хотел бы добавить еще одну мысль о том, что un-бокс следующих классов в их соответствующие примитивы также может генерировать исключение NullPointerException, если их соответствующие возвращенные объекты равны нулю.

  • byte - Byte
  • char - Персонаж
  • float - Float
  • int - Integer
  • long - Long
  • short - Short
  • double - Double

Вот код:

    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

Ответ 5

Способ понять это, когда вызывается Boolean.valueOf(null), java точно сказывается для вычисления нулевого значения.

Однако, когда вызывается Boolean.valueOf(modifiedItems.get("item1")), java говорят, чтобы получить значение из HashTable типа объекта Boolean, но он не находит тип Boolean, он находит тупик вместо (null), хотя он ожидал, что Boolean, Исключение NullPointerException выбрано потому, что создатели этой части java решили, что эта ситуация является примером того, что в программе происходит неправильно, что требует внимания программиста. (Произошло что-то непредвиденное.)

В этом случае разница между сознательным объявлением о том, что вы намереваетесь указать нуль, и java, обнаруживает недостающую ссылку на объект (нуль), где должен был быть найден объект.

Дополнительную информацию о NullPointerException в этом ответе: fooobar.com/info/55263/...