Ярлик оператора “or-assignment” (| =) в Java


89

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

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

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


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


13
Чому б вам не спробувати?
Роман

Це загальна логічна логіка, а не лише Java. так що ви можете шукати його в інших місцях. І чому б вам просто не спробувати?
Дикам 21.03.10

16
@Dykam: Ні, тут є специфічна поведінка. Java може вибрати | = коротке замикання, таким чином, що вона не буде оцінювати RHS, якщо LHS вже відповідає дійсності - але це не так.
Джон Скіт,

2
@Jon Skeet: Коротке замикання було б доречним для неіснуючого ||=оператора, але |=це комбінована форма побітового або оператора.
Девід Торнлі,

1
@ Джон Скіт: Звичайно, але робити |=коротке замикання буде несумісним з іншими операторами складеного присвоєння, оскільки a |= b;це не буде таким самим, як a = a | b;зі звичайним застереженням щодо aдвічі оцінки (якщо це важливо). Мені здається, що рішення про велику мовну поведінку не було ||=, тому я втрачаю вашу думку.
Девід Торнлі,

Відповіді:


204

|=Є оператором присвоювання з'єднання ( ПСБ 15.26.2 ) для логічного логічного оператора |( ПСБ 15.22.2 ); не плутати з умовно-або ||( JLS 15.24 ). Існують також &=і ^=відповідні складеному призначенню версія логічного логічного &і ^відповідно.

Іншими словами, для boolean b1, b2цих двох рівнозначно:

 b1 |= b2;
 b1 = b1 | b2;

Різниця між логічними операторами ( &і |) порівняно з їх умовними аналогами ( &&і ||) полягає в тому, що перші не "замикають"; останні роблять. Це є:

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

Отже, повертаючись до вашого початкового запитання, так, ця конструкція є дійсною, і хоча |=це не зовсім еквівалентний ярлик для, =і ||вона обчислює те, що ви хочете. Оскільки правою частиною |=оператора у вашому користуванні є проста операція порівняння цілих чисел, той факт, що |не замикається, є незначним.

Бувають випадки, коли коротке замикання бажане або навіть потрібне, але ваш сценарій не є одним із них.

Прикро, що на відміну від деяких інших мов, Java не має &&=і ||=. Це обговорювалось у питанні, чому Java не має складених версій присвоєння операторів conditional-and and conditional-or? (&& =, || =) .


+1, дуже ретельно. здається вірогідним, що компілятор цілком може перетворитися на оператора короткого замикання, якщо він зможе визначити, що RHS не має побічних ефектів. будь-яка підказка про це?
Карл,

Я читав, що коли RHS є тривіальним і SC не є необхідним, "розумні" оператори SC насправді трохи повільніші. Якщо це правда, то цікавіше задатися питанням, чи можуть деякі компілятори перетворити SC на NSC за певних обставин.
полігенмастильні матеріали

Оператори короткого замикання @polygenelubricants залучають якусь гілку під капотом, тому, якщо немає шаблону, передбаченого прогнозуванням гілок, до значень істини, що використовуються з операторами, та / або використовувана архітектура не має хорошого / жодного передбачення гілки ( і припускаючи, що компілятор та / або віртуальна машина не виконують ніяких пов'язаних з цим оптимізацій), тоді так, оператори SC вводять деяку повільність порівняно з не короткозамкненим. Найкращим способом дізнатись про те, що робить компілятор, було б скомпілювати програму за допомогою SC проти NSC, а потім порівняти байт-код, щоб побачити, чи відрізняється він.
JAB,

Крім того, не слід плутати з |
побітовим

16

Це не оператор "ярлика" (або короткого замикання) таким чином, що || та && є (оскільки вони не будуть оцінювати 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;

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


Я розпочав лише з одного, але, як було сказано в оригінальному запитанні, я відчув, що "рядок порівнянь був довгим і важким для читання, тому я розбив його для читабельності". Окрім цього, у цьому випадку мене більше цікавить вивчення поведінки | =, аніж змушення цієї конкретної частини коду працювати.
Девід Мейсон,

1

Хоча це може бути надмірним для вашої проблеми, бібліотека Guava має хороший синтаксис з Predicates і виконує оцінку короткого замикання або / і Predicates.

По суті, порівняння перетворюються на об’єкти, упаковуються у колекцію, а потім повторюються. Для або предикатів перше справжнє звернення повертається з ітерації, і навпаки для і.


1

Якщо мова йде про читабельність, у мене є концепція відокремлення перевірених даних від логіки тестування. Зразок коду:

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

Він читабельніший, ніж "рядок порівняння", а також має перевагу у виконанні короткого замикання (ціною виділення масиву та виклику методу).

Редагувати: Ще більшої читабельності можна просто досягти, використовуючи параметри varargs:

Підпис методу буде таким:

boolean checkIfAnyNegative(DataType ... data)

А дзвінок міг виглядати так:

checkIfAnyNegative( defaultStock, defaultWholesale, defaultRetail, defaultDelivery );

1
Розподіл масиву та виклик методу є досить великою вартістю короткого замикання, якщо у вас немає порівняно дорогих операцій (хоча приклад у питанні дешевий). Сказавши це, більшість випадків ремонтопридатність коду має перевагу щодо міркувань продуктивності. Я б, мабуть, використав щось подібне, якби я робив порівняння по-різному в купі різних місць або порівнював більше 4 значень, але для одного випадку це дещо багатослівно на мій смак.
Девід Мейсон,

@DavidMason Я згоден. Однак майте на увазі, що більшість сучасних калькуляторів проковтнули б такі витрати менше ніж на кілька міліс. Особисто я не піклувався б про накладні витрати, поки проблеми з продуктивністю не виявляться обґрунтованими . Також багатослівність коду є перевагою, особливо коли javadoc не надається або не генерується JAutodoc.
Krzysztof Jabłoński

1

Це стара публікація, але для того, щоб запропонувати інший погляд на початківців, я хотів би навести приклад.

Я думаю, що найпоширенішим варіантом використання подібного складеного оператора буде +=. Я впевнений, що ми всі писали щось подібне:

int a = 10;   // a = 10
a += 5;   // a = 15

Який сенс у цьому був? Суть полягала в тому, щоб уникнути шаблону та усунути повторюваний код.

Отже, наступний рядок робить точно те саме, уникаючи введення змінної b1двічі в одному рядку.

b1 |= b2;

1
Складніше виявити помилки в операції та іменах операторів, особливо коли імена довгі. longNameOfAccumulatorAVariable += 5; проти longNameOfAccumulatorAVariable = longNameOfAccumulatorVVariable + 5;
Андрія

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

Я вважаю, що я віддав би перевагу negativeValue = defaultStock < 0 || defaultWholesale < 0і т. Д. Окрім неефективності всього боксу та обгортань, що тут відбуваються, мені не так легко зрозуміти, що насправді означав би ваш код.
Джон Скіт,

Я маю навіть більше 4 параметрів, і критерій для них однаковий, тоді мені подобається моє рішення, але для читабельності я б розділив його на декілька рядків (тобто створити Список, знайти minvalue, порівняти minvalue з 0) .
Роман

Це справді виглядає корисним для великої кількості порівнянь. У цьому випадку я думаю, що зменшення чіткості не варто заощаджувати при наборі тексту тощо.
Девід Мейсон,

0

|| логічне логічне АБО
| побітове АБО

| = побітове включення АБО та оператор присвоєння

Причина, по якій | = не є короткою схемою, полягає в тому, що вона виконує побітове АБО не логічне АБО. Тобто:

C | = 2 те саме, що C = C | 2

Підручник для операторів Java


поразрядно АБО з логічними значеннями НІ! Оператор |є цілорозрядним АБО, але також є логічним АБО - див. Специфікацію мови Java 15.22.2.
user85421

Ви маєте рацію, оскільки для одного біта (логічного значення) побітове та логічне АБО еквівалентні. Хоча практично, результат однаковий.
oneklc
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.