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

Ярлык "или-присваивание" (| =) в Java

У меня есть длинный набор сравнений, который можно сделать на Java, и я хотел бы знать, если один или несколько из них выдаются как истинные. Строка сравнений была длинной и трудной для чтения, поэтому я разбил ее на читаемость и автоматически пошел использовать оператор быстрого доступа |=, а не negativeValue = negativeValue || boolean.

boolean negativeValue = false;
negativeValue |= (defaultStock < 0);
negativeValue |= (defaultWholesale < 0);
negativeValue |= (defaultRetail < 0);
negativeValue |= (defaultDelivery < 0);

Я ожидаю, что negativeValue будет истинным, если любое из значений по умолчанию <something> значения отрицательные. Действительно ли это? Будет ли это делать то, что я ожидаю? Я не мог видеть, что это упоминалось на Sun-сайте или stackoverflow, но Eclipse, похоже, не имеет с ним проблем, и код компилируется и запускается.


Аналогично, если бы я хотел выполнить несколько логических пересечений, могу ли я использовать &= вместо &&?

4b9b3361

Ответ 1

|= представляет собой сложный оператор присваивания (JLS 15.26.2) для логического логического оператора | (JLS 15.22.2); не следует путать с условным или || (JLS 15.24). Существуют также &= и ^=, соответствующие версии составного назначения логических логических & и ^ соответственно.

Другими словами, для boolean b1, b2 эти два эквивалентны:

 b1 |= b2;
 b1 = b1 | b2;

Разница между логическими операторами (& и |) по сравнению с их условными аналогами (&& и ||) заключается в том, что первые не являются "короткозамкнутыми"; последние делают. То есть:

  • & и | всегда оценивают оба операнда
  • && и || условно оценивать правый операнд; правый операнд оценивается только в том случае, если его значение может повлиять на результат двоичной операции. Это означает, что правый операнд НЕ оценивается, когда:
    • Левый операнд && оценивается как false
      • (поскольку независимо от того, что оценивает правильный операнд, все выражение false)
    • Левый операнд || оценивается как true
      • (поскольку независимо от того, что оценивает правильный операнд, все выражение true)

Итак, возвращаясь к исходному вопросу, да, эта конструкция действительна, и хотя |= не является эквивалентом ярлыка для = и ||, он вычисляет то, что вы хотите. Так как правая часть оператора |= в вашем использовании - простая операция сравнения целого числа, то факт, что | не является короткозамкнутым контуром, незначителен.

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

К сожалению, в отличие от некоторых других языков, Java не имеет &&= и ||=. Это обсуждалось в вопросе Почему Java не имеет составных версий условных и условных операторов или операторов? (& =, || =).

Ответ 2

Это не "короткий" (или короткозамкнутый) оператор таким образом, что || и && (в том, что они не будут оценивать RHS, если они уже знают результат на основе LHS), но он будет делать то, что вы хотите с точки зрения работы.

В качестве примера разницы этот код будет штраф, если text имеет значение null:

boolean nullOrEmpty = text == null || text.equals("")

тогда как это не будет:

boolean nullOrEmpty = false;
nullOrEmpty |= text == null;
nullOrEmpty |= text.equals(""); // Throws exception if text is null

(Очевидно, вы могли бы сделать "".equals(text) для этого конкретного случая - я просто пытаюсь продемонстрировать принцип.)

Ответ 3

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

boolean negativeValue
    = defaultStock < 0 
    | defaultWholesale < 0
    | defaultRetail < 0
    | defaultDelivery < 0;

Для простейших выражений использование | может быть быстрее, чем ||, потому что, несмотря на то, что оно избегает выполнения сравнения, это подразумевает использование ветки неявно и может быть во много раз дороже.

Ответ 4

Хотя это может быть излишним для вашей проблемы, библиотека Guava имеет хороший синтаксис с Predicate и делает короткое замыкание оценка или/и Predicate s.

По существу, сравнения превращаются в объекты, упакованные в коллекцию и затем повторяющиеся. Для или предикатов первое истинное попадание возвращается с итерации, и наоборот для и.

Ответ 5

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

// declare data
DataType [] dataToTest = new DataType[] {
    defaultStock,
    defaultWholesale,
    defaultRetail,
    defaultDelivery
}

// define logic
boolean checkIfAnyNegative(DataType [] data) {
    boolean negativeValue = false;
    int i = 0;
    while (!negativeValue && i < data.length) {
        negativeValue = data[i++] < 0;
    }
    return negativeValue;
}

Код выглядит более подробным и самоочевидным. Вы даже можете создать массив в вызове метода, например:

checkIfAnyNegative(new DataType[] {
    defaultStock,
    defaultWholesale,
    defaultRetail,
    defaultDelivery
});

Это более читаемая, чем "строка сравнения", а также имеет преимущество в производительности короткого замыкания (за счет распределения массива и вызова метода).

Edit: Еще более читабельность может быть достигнута просто с помощью параметров varargs:

Подпись метода:

boolean checkIfAnyNegative(DataType ... data)

И вызов может выглядеть так:

checkIfAnyNegative( defaultStock, defaultWholesale, defaultRetail, defaultDelivery );

Ответ 6

List<Integer> params = Arrays.asList (defaultStock, defaultWholesale, 
                                       defaultRetail, defaultDelivery);
int minParam = Collections.min (params);
negativeValue = minParam < 0;

Ответ 7

|| логическое логическое ИЛИ | побитовое ИЛИ

| = побитовое включение OR и оператор присваивания

Причина, по которой | = не shortcircit, состоит в том, что она выполняет поразрядное ИЛИ не логическое ИЛИ. То есть:

C |= 2 is same as C = C | 2

Учебник для java-операторов