Подтвердить что ты не робот

Является ли null типизированным в Java

Я столкнулся с ошибкой в ​​моей кодовой базе, я сузился к тому, что вызвало это поведение. Первый тестовый пример не выполняется, тогда как последние два успеха.

@Test
public void testBooleanNull1() {
    Boolean nullB = null;
    assertFalse(Boolean.valueOf(nullB));
}

@Test
public void testBooleanNull2() {
    String nullS = null;
    assertFalse(Boolean.valueOf(nullS));
}
@Test
public void testBooleanNull3() {
    assertFalse(Boolean.valueOf(null));
}

Я знаю, что Boolean.valueOf - это перегруженный метод с двумя вариантами, который принимает String, а другой принимает примитив типа boolean.

Я подозреваю, что это происходит из-за автоматического бокса, но я не уверен, что это так, кроме того, я не знаю, почему null преобразуется в boolean, насколько я знаю null не является допустимым типом primitive.

Я перешел к использованию BooleanUtils из Apache Commons, я попросил это здесь, чтобы лучше понять, почему поведение такое.

4b9b3361

Ответ 1

Это

Boolean.valueOf(nullB)

является вызовом Boolean#valueOf(boolean).

Это не удается, потому что при распаковке значения Boolean nullB происходит сбой NullPointerException. Другими словами, он становится

boolean val = nullB.booleanValue(); // at runtime nullB stores the value null
Boolean.valueOf(val)

Этот процесс описан в JLS здесь

Если r является ссылкой типа Boolean, то преобразование распаковки преобразуется r в r.booleanValue()

Это

Boolean.valueOf(null)

вызывает перегруженную версию которая принимает String (поскольку null не является допустимым выражением типа Boolean).

Возвращает a Boolean со значением, представленным указанной строкой. Возвращаемое значение Boolean представляет значение true, если аргумент строки равен не null и равен, игнорируя регистр, строке "true".

Ответ 2

Перегруженное разрешение метода описано в JLS:

15.12.2. Время компиляции Шаг 2: Определение сигнатуры метода

...

  • Первая фаза (§15.12.2.2) выполняет разрешение перегрузки без разрешения преобразования бокса или распаковки или использования вызова метода переменной arity. Если на этом этапе не обнаружен какой-либо применимый метод, обработка продолжается до второй фазы.

В нашем случае выбор между Boolean.valueOf(boolean) и Boolean.valueOf(String) компилятором выбирает valueOf(String), потому что он не требует распаковки.

Ответ 3

Взглянув на исходный код класса Boolean

public static Boolean valueOf(String s) {
    return toBoolean(s) ? TRUE : FALSE;
}

.. и затем:

private static boolean toBoolean(String name) {
   return ((name != null) && name.equalsIgnoreCase("true"));
}

Если аргумент String null для Boolean.valueOf(String s), он вернет false, поэтому второй случай преуспеет.

В первом случае при использовании Boolean.valueOf, который принимает тип данных как логический, вот что говорится в документации:

public static Boolean valueOf(boolean b)

Returns a Boolean instance representing the specified boolean value. If the specified boolean value is true, this method returns Boolean.TRUE; if it is false, this method returns Boolean.FALSE. If a new Boolean instance is not required, this method should generally be used in preference to the constructor Boolean(boolean), as this method is likely to yield significantly better space and time performance.

Parameters:
b - a boolean value.
Returns:
a Boolean instance representing b.

Здесь требуется параметр Boolean, который принимает значение True или False, а не Boolean, которое принимает True, False или null. Поскольку вы передали значение Boolean, а не Boolean, я предполагаю, что причина, по которой он терпит неудачу.

Ответ 4

Почему второй будет успешным? Посмотрите на документ public static Boolean valueOf(String s):

Возвращает логическое значение со значением, представленным указанной строкой.     Возвращаемое значение Boolean представляет истинное значение, если строковый аргумент не null и равен, игнорируя регистр, строке "true".

Вы передали null, поэтому он вернет false.