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


227
String x = (String) null;

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

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

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


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

Ответы:


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

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


В большинстве случаев приведение неявно, например 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);
}

5

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

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

и

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

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

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

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


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


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 без исключения. Неважно, что это за класс, ноль - ноль.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.