Можно ли вычислить первый аргумент в тернарном операторе Java, даже если выражение выдает ложное значение? - PullRequest
13 голосов
/ 07 февраля 2011

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

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

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

Теперь вот метод, который вводит в заблуждение. Вызов метода test для этого метода приводит к 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 () вызывается через автобокс, что приводит к исключению NullPointerException для нулевого значения. Благодаря отладке известно, что findSwapperToUse () не вызывается.

Однако по этому вопросу этого не должно происходить: Java троичная (немедленное if) оценка

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

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

Что, черт возьми, происходит?

РЕДАКТИРОВАТЬ: я забыл добавить, что я изменил код, чтобы избежать этого с помощью прямого оператора if - следующий код работает как ожидалось:

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

Ответы [ 2 ]

22 голосов
/ 07 февраля 2011

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

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

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

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

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

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

A != null ? (Integer) Swapper.findSwapperToUse(A).c : B
0 голосов
/ 07 февраля 2011

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

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

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