Не виняток під час набору кастингу з нулем у 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.