Не виняток під час набору кастингу з нулем у Java


227
String x = (String) null;

Чому в цьому твердженні немає винятку?

String x = null;
System.out.println(x);

Це друкує null. Але .toString()метод повинен викинути нульовий виняток вказівника.


про який виняток ви говорите, компілятор?
Sajan Chandran

Відповіді:


323

Ви можете передавати nullбудь-який тип посилань, не отримуючи винятків.

printlnМетод не кидає порожній покажчик , оскільки він спочатку перевіряє , чи є об'єкт порожнім чи ні. Якщо null, то він просто надрукує рядок "null". Інакше він викличе toStringметод цього об’єкта.

Додавання додаткової інформації:String.valueOf(object) Метод виклику внутрішньо методом друку на вхідному об'єкті. І в valueOfметоді ця перевірка допомагає уникнути нульового виключення вказівника:

return (obj == null) ? "null" : obj.toString();

Для решти вашої плутанини, виклик будь-якого методу на нульовому об'єкті повинен викинути нульовий виняток вказівника, якщо не окремий випадок.


1
@JunedAhsan, які особливі випадки спричинить його не кидання NPE?
Холлоуей

@Trengot Перевірте згадане у відповіді Пітера нижче.
Juned Ahsan

144

Ви можете nullнадати будь-який тип посилання. Ви також можете викликати методи, які обробляють a nullяк аргумент, наприклад, System.out.println(Object)так, але ви не можете посилатися на nullзначення та викликати метод.

BTW Існує складна ситуація, коли, схоже, ви можете викликати статичні методи на nullзначення.

Thread t = null;
t.yield(); // Calls static method Thread.yield() so this runs fine.

12
Ого. Якщо б запитали, я був би на 100% впевнений, що це призведе до виключення. Справді, хитра справа.
Magnilex

2
@Magnilex: Абсолютно! це типове запитання про випробування OCPJP.
ccpizza

3
Це не тому, що компілятор у байт-коді t.yield() -> Thread.yeld() все одно "оптимізує" його ? Подібно до того, як final int i = 1; while (i == 1)оптимізовано доwhile(true)
SGal

@SGal Це ще краще, тому що друга оптимізація - AFAIK не є обов'язковою, тоді як перша є якоюсь.
Пол

36

Це за дизайном. Ви можете nullнадати будь-який тип посилання. Інакше ви не зможете призначити його посилальним змінним.


Дякую! ця примітка щодо присвоєння нульовим еталонним змінним дуже корисна!
Макс

22

Кастинг нульових значень потрібен для наступної конструкції, коли метод перевантажений, і якщо нуль передається цим перевантаженим методам, тоді компілятор не знає, як усунути неоднозначність, отже, нам потрібно набрати нуль у цих випадках:

class A {
  public void foo(Long l) {
    // do something with l
  }
  public void foo(String s) {
    // do something with s      
  }
}
new A().foo((String)null);
new A().foo((Long)null);

Інакше ви не зможете викликати потрібний вам метод.


У більшості випадків кастинг неявний, наприклад, String bar = null;передає nullзначення String. Поки що мені довелося явно наводити null в тесті, де метод був перевантажений, і я хотів перевірити його поведінку з введенням null. І все-таки, добре знати, я збирався написати подібну відповідь, перш ніж знайшов вашу.
Власек

Веселий факт: l instanceof Longі s instanceof Stringповернеться falseв цих випадках.
Аттіла Таній

7

Println(Object) використовує String.valueOf()

public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}

Print(String) робить нульову перевірку.

public void print(String s) {
    if (s == null) {
        s = "null";
    }
    write(s);
}

5

Багато відповідей тут уже згадуються

Ви можете привести нуль до будь-якого типу посилань

і

Якщо аргумент є нульовим, то рядок, рівний "null"

Я цікавився, де це вказано, і подивився його на специфікацію Java:

Нульова посилання завжди може бути призначена або передана будь-якому типу посилань (§5.2, §5.3, §5.5).

Якщо посилання є нульовим, воно перетворюється на рядок "null" (чотири ASCII символи n, u, l, l).


3

Як написали інші, ви можете припустити нуль до всього. Зазвичай вам це не потрібно, ви можете написати:

String nullString = null;

не ставлячи туди ролі.

Але бувають випадки, коли такі ролі мають сенс:

а) якщо ви хочете переконатися, що викликається певний метод, наприклад:

void foo(String bar) {  ... }
void foo(Object bar) {  ... }

то це змінило б значення, якщо ви наберете

foo((String) null) vs. foo(null)

б) якщо ви плануєте використовувати свій IDE для створення коду; наприклад, я зазвичай пишу одиничні тести, такі як:

@Test(expected=NullPointerException.class)
public testCtorWithNullWhatever() {
    new MyClassUnderTest((Whatever) null);
}

Я роблю TDD; це означає, що клас "MyClassUnderTest", ймовірно, ще не існує. Записавши цей код, я можу потім використовувати свій IDE, щоб спочатку генерувати новий клас; а потім генерувати конструктор, який приймає аргумент "Що б не було", - IDE може зрозуміти з мого тесту, що конструктор повинен приймати рівно один аргумент типу "Що б не було".


2

Друк :

Роздрукуйте об’єкт. Рядок, створений методом String.valueOf (Object), переводиться в байти

ValueOf :

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

Він просто поверне рядок зі значенням "null", коли об'єкт є null.


2

Це дуже зручно при використанні методу, який інакше був би неоднозначним. Наприклад: JDialog має конструктори з такими підписами:

JDialog(Frame, String, boolean, GraphicsConfiguration)
JDialog(Dialog, String, boolean, GraphicsConfiguration)

Мені потрібно використовувати цей конструктор, тому що я хочу встановити GraphicsConfiguration, але у мене немає батьків для цього діалогового вікна, тому перший аргумент повинен бути нульовим. Використання

JDialog(null, String, boolean, Graphicsconfiguration) 

є неоднозначним, тому в цьому випадку я можу звузити виклик, передавши null до одного з підтримуваних типів:

JDialog((Frame) null, String, boolean, GraphicsConfiguration)

0

Ця мовна функція зручна в цій ситуації.

public String getName() {
  return (String) memberHashMap.get("Name");
}

Якщо memberHashMap.get ("Ім'я") повертає null, ви все одно хочете, щоб метод, наведений вище, повернув null, не викидаючи виняток. Незалежно від класу, null є null.

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