Чому String.valueOf (null) кидає NullPointerException?


127

згідно з документацією, метод String.valueOf(Object obj)повертає:

якщо аргумент є null, то рядок дорівнює "null"; в іншому випадку obj.toString()повертається значення.

Але як прийти, коли я спробую це зробити:

System.out.println("String.valueOf(null) = " + String.valueOf(null));

він замість цього кидає NPE? (спробуйте самі, якщо не вірите!)

    Виняток у потоці "main" java.lang.NullPointerException
    на java.lang.String. (невідоме джерело)
    на java.lang.String.valueOf (невідоме джерело)

Як це відбувається? Чи лежить мені документація? Це головна помилка на Java?

Відповіді:


201

Проблема полягає в тому, що String.valueOfметод перевантажений :

Мова специфікації Java передбачає, що в таких випадках вибирається найбільш специфічне перевантаження :

JLS 15.12.2.5 Вибір найбільш конкретного методу

Якщо для виклику методу доступний і застосовний більш ніж один член-метод, необхідно вибрати один, щоб надати дескриптор для відправки методу виконання. Мова програмування Java використовує правило, що обирається найбільш конкретний метод.

А char[] є -а Object , але не все Object є -а char[] . Таким чином, char[]є більш точним , ніж Object, і , як зазначено на мові Java, то String.valueOf(char[])перевантаження вибирається в цьому випадку.

String.valueOf(char[])очікує, що масив буде не- null, і оскільки nullвін дається в цьому випадку, він потім кидає NullPointerException.

Легке "виправлення" полягає в тому, щоб nullявно передати Objectнаступне:

System.out.println(String.valueOf((Object) null));
// prints "null"

Пов'язані питання


Мораль розповіді

Є кілька важливих:

  • Ефективне Java 2-е видання, пункт 41: Використовуйте перевантаження з розумом
    • Тільки тому, що ви можете перевантажуватись, не означає, що вам слід кожного разу
    • Вони можуть спричинити плутанину (особливо якщо методи роблять різну річ)
  • Використовуючи хороший IDE, ви можете перевірити, яке перевантаження вибрано під час компіляції
    • З Eclipse, ви можете миші при наведенні курсору на наведеному вище виразі , і бачимо , що на насправді , то valueOf(char[])вибирається перевантаження!
  • Іноді ви хочете, щоб явно подати null(приклади для перегляду)

Дивитися також


На кастингу null

Існують щонайменше дві ситуації, коли необхідне чітке перенесення nullна певний тип посилання:

  • Вибір перевантаження (як наведено у наведеному вище прикладі)
  • Надати nullв якості єдиного аргументу параметр vararg

Простий приклад останнього:

static void vararg(Object... os) {
    System.out.println(os.length);
}

Тоді ми можемо мати наступне:

vararg(null, null, null); // prints "3"
vararg(null, null);       // prints "2"
vararg(null);             // throws NullPointerException!

vararg((Object) null);    // prints "1"

Дивитися також

Пов'язані питання


5
Ще одна пропозиція: Коли ви перевантажуєте метод, і один аргумент може бути викликаний двома перевантаженнями (наприклад, nullу цьому випадку), переконайтеся, що обидва перевантаження діють однаково щодо цього значення!
Йоахім Зауер

3
@Joachim: Я щойно прочитав пункт і був приємно здивований, виявивши, що ці два методи були чітко обговорені! Блох пішов на крок далі і стверджує, що оскільки String.valueOf(Object)і так valueOf(char[])чи інакше робиш різні речі (незалежно від того nullчи ні), що, можливо, вони не повинні були перевантажувати в першу чергу.
полігенмастильні матеріали

У вас є питання ... якщо у вас є метод, який повертає Object, тоді твердження return null;буде точно таким же, як і return (Object) null;?
користувач972276

17

Проблема полягає в тому, що ви телефонуєте, String.valueOf(char[])а не String.valueOf(Object) .

Причиною цього є те, що Java завжди вибере найбільш конкретну версію перевантаженого методу, яка працює з заданими параметрами. nullє допустимим значенням Objectпараметра, але воно також є дійсним значенням для char[]параметра.

Щоб змусити Java використовувати Objectверсію, перейдіть nullчерез змінну або вкажіть явний формат для Object:

Object o = null;
System.out.println("String.valueOf(null) = " + String.valueOf(o));
// or
System.out.println("String.valueOf(null) = " + String.valueOf((Object) null));

5

Помилка під номером 4867608 була подана таким чином ще в 2003 році, що було вирішено як "не виправить" за допомогою цього пояснення.

Ми не можемо змінити це через обмеження сумісності. Зауважте, що саме метод відкритого статичного String valueOf (char data []) в кінцевому підсумку викликається, і він не згадує про заміну «null» на null аргументи.

@ ###. ### 2003-05-23


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