Нет исключений при приведении типа с нулем в Java

227
String x = (String) null;

Почему нет исключения в этом утверждении?

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

Это печатает null. Но .toString()метод должен выдать исключение нулевого указателя.

Vicky
источник
о каком исключении ты говоришь, компилятор?
Саджан Чандран

Ответы:

323

Вы можете привести nullк любому типу ссылки без каких-либо исключений.

printlnМетод не бросает пустой указатель , поскольку он сначала проверяет , является ли объект пустым или нет. Если ноль, то он просто печатает строку "null". В противном случае он вызовет toStringметод этого объекта.

Добавление более подробной информации: внутренняя печать методов вызывает String.valueOf(object)метод для объекта ввода. И в valueOfметоде эта проверка помогает избежать исключения нулевого указателя:

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

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

Джунед Асан
источник
1
@JunedAhsan, какие особые случаи заставили бы его не бросать NPE?
Holloway
@Trengot Отметьте один из упомянутых в Питере ответ ниже.
Juned Ahsan
144

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

Кстати, есть сложная ситуация, когда кажется, что вы можете вызывать статические методы для 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

Преобразование значений NULL требуется для следующей конструкции, где метод перегружен, и если значение NULL передается этим перегруженным методам, компилятор не знает, как устранить неоднозначность, поэтому нам необходимо типизировать NULL в следующих случаях:

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);

В противном случае вы не могли бы вызвать метод, который вам нужен.

Mewel
источник
В большинстве случаев приведение неявно, например String bar = null;приведение nullзначения к String. До сих пор мне приходилось только явно приводить ноль в тесте, где метод был перегружен, и я хотел проверить его поведение с нулевым вводом. Тем не менее, приятно знать, я собирался написать аналогичный ответ, прежде чем я нашел ваш.
Власек
Интересный факт: 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);
}
Rocketboy
источник
5

Многие ответы здесь уже упоминают

Вы можете привести null к любому типу ссылки.

и

Если аргумент равен нулю, то строка равна нулю

Я задавался вопросом, где это указано, и посмотрел его в спецификации Java:

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

Если ссылка имеет значение null, она преобразуется в строку «null» (четыре символа ASCII n, u, l, l).

Arigion
источник
3

Как написали другие, вы можете бросить null на все. Обычно вам это не нужно, вы можете написать:

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 может из моего теста выяснить, что конструктор должен принимать ровно один аргумент типа «безотносительно».

GhostCat
источник
2

Печать :

Распечатать объект. Строка, созданная методом String.valueOf (Object), переводится в байты.

ValueOf :

если аргумент равен нулю, то строка равна нулю; в противном случае возвращается значение 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 ("Name") возвращает значение null, вы все равно хотите, чтобы описанный выше метод возвращал значение null без исключения. Неважно, что это за класс, ноль - ноль.

Песня Чанггула
источник