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
    }
}

Моя проблема полягає в тому, що я не розумію, чому тест 3 працює добре (він друкує falseі не видає NullPointerException), тим часом тест 4 кидає NullPointerException. Як ви можете бачити в тестах 1 і 2 , nullі modifiedItems.get("item1")рівні і null.

Поведінка однакова у Java 7 та 8.


modifiedItems.get ("item1") це недійсне значення, ви це знаєте, але ви припускаєте, що передача цього значення valueF не закінчиться в NPE?
Стултуске

16
@Stultuske: Це правильне питання, враховуючи, що лише два рядки вище передачі буквалу nullна ту ж функцію не генерують NPE! Для цього є вагомі причини, але це, звичайно, незрозуміле на перший погляд :-)
psmears

25
Я вражений. Це найцікавіше питання про виключення з нульовим покажчиком, яке я бачив за роки.
candied_orange

@Jeroen це не справжнє питання . Хоча це правда, що розпакування є загальним для двох проблем, тут порівняння не відбувається. Ключовим у цьому питанні є те, що воно виникає через спосіб вирішення перевантажень; і це зовсім інша річ від способу ==застосування.
Енді Тернер

Відповіді:


178

Ви повинні уважно ознайомитися з тим, на яке виклик перевантаження:

  • Boolean.valueOf(null)викликає Boolean.valueOf(String). Це не кидає NPEнавіть, якщо він постачається з нульовим параметром.
  • Boolean.valueOf(modifiedItems.get("item1"))викликає Boolean.valueOf(boolean), тому modifiedItemsщо значення s є типом Boolean, для чого потрібна конверсія в unboxing. Так як modifiedItems.get("item1")це null, це розпакування цього значення - НЕ Boolean.valueOf(...)- який викидає NPE.

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

  • У першому проході шукається збіг методів, не допускаючи боксу / розпакування (ні методів змінної арності).

    • Оскільки nullце прийнятне значення для, Stringале ні boolean, Boolean.valueOf(null)воно відповідає Boolean.valueOf(String)цьому пропуску;
    • Booleanне є прийнятним для будь-якої Boolean.valueOf(String)або Boolean.valueOf(boolean), так що ні один метод не узгоджений в цьому проході для Boolean.valueOf(modifiedItems.get("item1")).
  • У другому проході шукається збіг методів, що дозволяє боксувати / розпаковувати (але все ще не мінливі методи арності).

    • A Booleanможе бути без коду boolean, тому Boolean.valueOf(boolean)він узгоджується з Boolean.valueOf(modifiedItems.get("item1"))цим проходом; але перекладач для розблокування повинен вставити компілятор, щоб викликати його:Boolean.valueOf(modifiedItems.get("item1").booleanValue())
  • (Є третій пропуск, який дозволяє застосовувати методи змінної арності, але це не актуально, оскільки перші два проходи відповідають цим випадкам)


3
Чи може бути код більш зрозумілим, якщо ми використовуємо Boolean.valueOf(modifiedItems.get("item1").booleanValue())замість нього вихідний код Boolean.valueOf(modifiedItems.get("item1"))?
ПричиняєПотокиПовсюди

1
@CausingUnderflowsEverywhere не насправді - насправді важко помітити, що .booleanValue()поховано в виразі. Два спостереження: 1) автоматичний (не) бокс - це цілеспрямована особливість Java для видалення синтаксичного суглоба; зробити це самостійно можливо, але не ідіоматично; 2) це зовсім не допомагає вам - це, безумовно, не зупиняє виникнення проблеми, а також не дає додаткової інформації, коли відбувається збій (слід стека буде ідентичним, тому що виконаний код ідентичний).
Енді Тернер

@CausingUnderflowsEverywhere краще використовувати інструменти для висвітлення проблем, наприклад, intellij заробив би вас тут щодо потенційних NPE.
Енді Тернер

13

Оскільки modifiedItems.getповертає a Boolean(який не можна віддати в a String), підпис, який буде використаний, є Boolean.valueOf(boolean), де Booleanвиводиться на примітив 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


Неправильне формулювання, спасибі за вказівку та відповідь оновлюється після ваших відгуків. Вибачте, я не бачив вашої відповіді під час написання і бачу, що моя схожа на вашу. Чи слід видалити свою відповідь, щоб уникнути плутанини щодо ОП?
Аль-ун

4
Не видаляйте його з мого облікового запису. Пам’ятайте, це не гра з нульовою сумою: люди можуть (і взагалі) відкликати кілька відповідей.
Енді Тернер

3

Як Енді вже дуже добре описав причину NullPointerException:

що пов'язано з булевим un-boxing:

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

перетворюйте на:

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

під час виконання, а потім він кидає, NullPointerExceptionякщо modifiedItems.get("item1")є нульовим.

Тепер я хотів би додати тут ще один пункт, що скасування боксу наступних класів для відповідних примітивів також може створити NullPointerExceptionвиняток, якщо відповідні їм повернуті об'єкти недійсні.

  1. байт - байт
  2. char - Характер
  3. float - Float
  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)викликається, ява точно підказується для оцінки нуля.

Однак, коли Boolean.valueOf(modifiedItems.get("item1"))виклик, Java повідомляється отримати значення з HashTable об'єкта типу Boolean, але він не знаходить типу Boolean, а знаходить тупик замість (null), хоча він очікував Boolean. Виняток NullPointerException кидається тому, що творці цієї частини Java вирішили, що ця ситуація є випадком того, що в програмі відбувається щось не так, що потребує уваги програміста. (Сталося щось ненавмисне.)

У цьому випадку більша відмінність між навмисним оголошенням того, що ви призначали, що нуль знаходиться там, і java пошуку відсутньої посилання на об'єкт (null), де об'єкт мав бути знайдений.

Дивіться більше інформації про NullPointerException у цій відповіді: https://stackoverflow.com/a/25721181/4425643


Якщо хтось може допомогти покращити цю відповідь, я думав про слово, яке стосується програміста, що пише щось із чітким наміром, без двозначності
CausingUnderflowsEverywhere
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.