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

Недавно я обнаружил необычную ошибку в моем коде случайным ad-hoc-тестированием. Итак, я сделал для этого тестовый пример.

Вот мой тестовый пример:

 SampleRequest request = new SampleRequest();
    request.setA(null);
    request.setB(null);
    assertEquals(null, request.getAOrB());

A и B определяются как типы java.lang.Integer и имеют прямые методы настройки, чтобы установить их значения в запрос.

Существует также перечисление. Он имеет примитивное целочисленное значение и метод, используемый в этом коде. Я разместил здесь следующие разделы:

enum Swapper {
public int c;
Swapper findSwapperToUse(final int a) {
   for(Swapper swapper : values()) {
       if(swapper.c == a) {
          return swapper;
       }
   }
   return null;
}
}

Теперь, здесь метод, который запутывает. Вызов метода теста для этого метода приводит к NPE, но в последней строке метода.

    public class SampleRequest {
    private Integer A;
    private Integer B;

    public void setA(final Integer A) {
        this.A = A;
    }

    public void setB(final Integer B) {
        this.B = B;
    }


public Integer getAOrB() {
    return A != null ? Swapper.findSwapperToUse(A).c
         : B;
}
}

В тесте оба A и B установлены в нуль. Следовательно, A!= Null возвращает false. Тем не менее, я получаю исключение NullPointerException в номере строки для строки: B.

Я предполагаю, что по какой-то причине оценивается первое выражение Swapper.findSwapperToUse(A).c, и поэтому A.intValue() вызывается с помощью autoboxing, в результате чего NullPointerException имеет значение null. Благодаря отладке известно, что findSwapperToUse() не вызывается.

Однако, согласно этому вопросу, этого не должно произойти: Явная трехмерная (немедленная) оценка

Выраженное выражение операнда не оценивается для этой конкретной оценки условного выражения.

Возврат null (B) не приведет к исключению NullPointerException - он отлично подходит для возврата здесь нулевого результата.

Что происходит?

EDIT: Я забыл добавить, что я изменил код, чтобы избежать этого, используя оператор "прямо вверх" if - следующий код работает как ожидалось:

public Integer getAOrB() {
    if(A != null) {
        return Swapper.findSwapperToUse(A).c;
    }
    return B;
}

Ответы

Ответ 1

Я думаю, проблема связана с тем, что компилятор выводит тип всего выражения

A != null ? Swapper.findSwapperToUse(A).c : B

как int из типа Swapper.c, и поэтому пытается применить преобразование unboxing в B.

Вот связанная выдержка из JLS, §15.25:

  • В противном случае, если второй и третий операнды имеют типы, которые могут быть конвертируемыми (§5.1.8) к числовым типам, то есть несколько случаев:
    • ...
    • В противном случае к операнду применяется двоичное числовое продвижение (§5.6.2) типы, а тип условного выражения - это продвинутый тип второго и третьего операндов. Обратите внимание, что выполняется двоичное числовое продвижение unboxing conversion (§5.1.8) и преобразование значений (§5.1.13).

Вы можете предотвратить это, добавив следующий состав:

A != null ? (Integer) Swapper.findSwapperToUse(A).c : B

Ответ 2

Ваш метод findSwapperToUse возвращает значение null, и вы не можете сделать null.c.

Чтобы убедиться в этом, я бы изменил ваш код:

public Integer getAOrB() {
    if(A != null) {
        Swapper foundSwapper = Swapper.findSwapperToUse(A);
        return foundSwapper.c;
    }
    return B;
}