Чому у Java немає складених версій присвоєння операторів conditional-and and conditional-or? (&& =, || =)


83

Так що для бінарних операторів в булевих, Java має &, |, ^, &&і ||.

Давайте коротко підсумуємо, що вони тут коротко роблять:

Адже &значення результату - це, trueякщо обидва значення операнда є true; в іншому випадку результат є false.

Адже |значення результату - це, falseякщо обидва значення операнда є false; в іншому випадку результат є true.

Бо ^значення результату - це trueякщо значення операнда різні; в іншому випадку результат є false.

&&Оператор подібний , &але оцінює її правий операнд тільки , якщо значення його лівого операнда є true.

||Оператор подібний |, але оцінює її правий операнд тільки , якщо значення його лівого операнда є false.

Зараз серед усіх 5, 3 з них мають складені версії присвоєння, а саме |=, &=і ^=. Тож моє запитання очевидне: чому Java не надає, &&=а ||=також? Я вважаю , що мені потрібно більше , ніж ті , які мені потрібні &=і |=.

І я не думаю, що "тому що це занадто довго" - це хороша відповідь, тому що Java має >>>=. Для цього упущення має бути краща причина.


З 15.26 Оператори присвоєння :

Існує 12 операторів присвоєння; [...]= *= /= %= += -= <<= >>= >>>= &= ^= |=


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

З 15.26.2 Оператори складеного призначення :

Складений вираз присвоєння форми E1 op= E2еквівалентний E1 = (T)((E1) op (E2)), де T- тип E1, за винятком того, що E1обчислюється лише один раз.

Як доказ, наступний фрагмент кидає a NullPointerException, а не an ArrayIndexOutOfBoundsException.

    int[] a = null;
    int[] b = {};
    a[0] += b[-1];

2
Я переходжу до другого, нікого не хвилює: P також, усі ці питання про "чому особливість x не в мові y?" слід звертатися до дизайнерів мови, а не до нас: P
Federico klez Culloca

1
Що означає & =? Хтось, будь ласка, може мені сказати?
Тарік

@ Аарон: a = a & b. Це написано у запитанні
Federico klez Culloca


1
@jleedev: Це питання давніше, але тут більше голосів та вхідних посилань. Я б сказав, якщо відбудеться злиття, об’єднайте старе з цим (так, це можна зробити).
полігенні мастила

Відповіді:


28

Причина

Оператори &&=та ||=недоступні на Java, оскільки для більшості розробників такими операторами є:

  • схильний помилятися
  • марно

Приклад для &&=

Якщо Java дозволена &&=оператору, то цей код:

bool isOk = true; //becomes false when at least a function returns false
isOK &&= f1();
isOK &&= f2(); //we may expect f2() is called whatever the f1() returned value

було б еквівалентно:

bool isOk = true;
if (isOK) isOk = f1();
if (isOK) isOk = f2(); //f2() is called only when f1() returns true

Цей перший код схильний до помилок, оскільки багато розробників вважають, що f2()його завжди називають незалежно від поверненого значення f1 (). Це як bool isOk = f1() && f2();де f2()викликається лише тоді, коли f1()повертається true.

Якщо розробник хоче, f2()щоб його викликали лише тоді, коли він f1()повертається true, отже другий код вище менш схильний до помилок.

В іншому &=достатньо, оскільки розробник хоче, f2()щоб його завжди називали:

Той самий приклад, але для &=

bool isOk = true;
isOK &= f1();
isOK &= f2(); //f2() always called whatever the f1() returned value

Більше того, JVM повинен запускати цей наведений вище код як такий:

bool isOk = true;
if (!f1())  isOk = false;
if (!f2())  isOk = false;  //f2() always called

Порівняйте &&та отримайте &результати

Чи є результати операторів &&і &той же , коли застосовується на булевих значень?

Давайте перевіримо, використовуючи такий код Java:

public class qalcdo {

    public static void main (String[] args) {
        test (true,  true);
        test (true,  false);
        test (false, false);
        test (false, true);
    }

    private static void test (boolean a, boolean b) {
        System.out.println (counter++ +  ") a=" + a + " and b=" + b);
        System.out.println ("a && b = " + (a && b));
        System.out.println ("a & b = "  + (a & b));
        System.out.println ("======================");
    }

    private static int counter = 1;
}

Вихід:

1) a=true and b=true
a && b = true
a & b = true
======================
2) a=true and b=false
a && b = false
a & b = false
======================
3) a=false and b=false
a && b = false
a & b = false
======================
4) a=false and b=true
a && b = false
a & b = false
======================

Тому ТАК , ми можемо замінити &&на &для логічних значень ;-)

Так що краще використовувати &=замість &&=.

Те саме для ||=

Ті самі причини, що і для &&=:
оператор |=менш схильний до помилок, ніж ||=.

Якщо розробник хоче, f2()щоб його не викликали при f1()поверненні true, я раджу наступні альтернативи:

// here a comment is required to explain that 
// f2() is not called when f1() returns false, and so on...
bool isOk = f1() || f2() || f3() || f4();

або:

// here the following comments are not required 
// (the code is enough understandable)
bool isOk = false;
if (!isOK) isOk = f1();
if (!isOK) isOk = f2(); //f2() is not called when f1() returns false
if (!isOK) isOk = f3(); //f3() is not called when f1() or f2() return false
if (!isOK) isOk = f4(); //f4() is not called when ...

1
Привіт @StriplingWarrior. Я проконсультувався зі своїм колегою Янніком, нашим найкращим експертом з Java. Я оновив свою відповідь, використовуючи джерело коду Java, що використовується для перевірки цього пункту. Як ви вже сказали, &і &&дайте ті самі результати. Дякуємо за Ваш відгук. Вам подобається моя відповідь? Ура.
oHo

2
Що робити, якщо я хочу зробити це дуже швидко? && = був би швидшим за & =, якби він існував, тож вам слід використовувати if (a) a = bдля швидкості
adventurerOK

Привіт @adventurerOK. На жаль, я не впевнений, що розумію, що ви маєте на увазі ... Я думаю, a&=b;це швидше, ніж if(a) a=b;при використанні значень, що зберігаються в регістрах ЦП. Однак, якщо він bзнаходиться у зовнішній пам'яті (не кешована), то if(a) a=b;це швидше. Це ти маєш на увазі? Будь ласка, надайте більше прикладу коду ;-) Мені цікаво ваша думка. Побачимось. Привітання
Ого

13
Я не згоден, коли ти кажеш "І це не те, що ми хочемо". Якщо я напишу, isOK &&= f2();я хотів би, щоб це замикало так само, як &&це робить.
Тор Клінгберг,

4
Я не погоджуюся з вашим твердженням, що оператори будуть схильні до помилок або марні. Використання складених завдань - це те, що ви робите, щоб скористатися ярликом для звичайного A = A op B, тому кожен прекрасно знає, що робить, і може сам піклуватися про наслідки. Якби ваші причини справді були причиною його відсутності, я б розцінив це як небажане протегування. Однак я хочу подякувати вам за додавання рядка, bool isOk = f1() || f2() || f3() || f4();бо саме це я мав сліпити, щоб побачити себе.
Франц Б.

17

Можливо тому, що щось на зразок

x = false;
x &&= someComplexExpression();

схоже, це повинно бути присвоєння xта оцінка someComplexExpression(), але той факт, що оцінка залежить від вартостіx , не видно з синтаксису.

Також тому, що синтаксис Java базується на C, і ніхто не бачив нагальної потреби додавати ці оператори. Вам, мабуть, було б краще із заявою if, у будь-якому випадку.


38
Я думаю, що це не є гарною відповіддю, тому що можна стверджувати, що, x() && y() схоже, це повинно оцінювати обидві сторони виразу. Очевидно, що люди сприймають &&коротке замикання, тому це повинно також слідувати &&=.
полігенмастильні речовини

2
@jleedev, погодився. Я вважаю, що в цих ситуаціях важливо пам’ятати, що це не еквівалент x = x && someComplexExpression (), а еквівалент x = someComplexExpression () && x. Права частина буде / повинна бути оцінена спочатку, щоб узгодити її з кожним іншим оператором присвоєння. А враховуючи це, && = не буде відрізнятися від & =.
PS, швидкість

@polygene Це справедливо. Однак те, як щось виглядає, базується на тому, що ви знайомі, і, мабуть, вони не хотіли додавати нічого нового. Мені подобається в C / C ++ / Java / C # те, що основний синтаксис виразів майже однаковий.
Джош Лі

1
@PSpeed, ти помиляєшся. JLS дуже чітко розуміє, що має виконувати складене призначення. Дивіться моє доповнення вище.
полігенмастильні речовини

1
@PSpeed: У такому випадку це a -= b;буде працювати зовсім інакше, ніж a &&= b;.
Девід Торнлі,

11

Це так у Java, бо саме так у C.

Тепер питання, чому це так в С, полягає в тому, що коли & та && стали різними операторами (колись передуючи спуску С з В), різноманітність операторів & = просто не помічали.

Але у другій частині моєї відповіді немає жодних джерел, що підтверджують це.


1
Зі смішного боку - це питання нещодавно задавали на форумі С; і ця відповідь була зв’язана (хоча не позначена дублікатом), що робить завершення кругового аргументу!
UKMonkey

6

Значною мірою тому, що синтаксис Java базується на C (або принаймні на сімействі C), і в C всі ці оператори присвоєння компілюються в арифметичні або побітові інструкції збірки в одному регістрі. Версія оператора присвоєння уникає тимчасових подій і, можливо, створила більш ефективний код для ранніх неоптимізуючих компіляторів. Логічний оператор (як вони називаються на С) еквіваленти ( &&=і||= ) не мають такої очевидної відповідності єдиним інструкціям зі складання; вони зазвичай розширюються до тестової та розгалуженої послідовності інструкцій.

Цікаво, що такі мови, як рубін , мають || = та && =.

Редагувати: термінологія відрізняється між Java та C


Я вважаю, ви маєте на увазі, що умовні еквіваленти оператора не мають такої очевидної відповідності. У термінології JLS, булеві логічні оператори &, |і ^; &&і ||є умовними операторами.
полігенмастильні речовини

За термінологією C, && та || є "логічними операторами", s6.5.13-14 в ISO 9899: 1999. Побітові оператори "логічні" лише тоді, коли застосовуються до одного біта (логічне значення в Java); в C немає однобітового типу, і логічні оператори там застосовуються до всіх скалярних типів.
p00ya

до C99 у C навіть не було boolтипу. Це 20 років історії мови без жодних помилок. Причин не було &&=. побітових операцій було досить.
v.oddou

4

Однією з початкових цілей Java було бути "Простий, об'єктно-орієнтований і звичний". Що стосується цього випадку, & = є знайомим (C, C ++ це має і знайоме в цьому контексті означає знайоме тому, хто знає цих двох).

&& = не було б знайомим і не було б простим, в тому сенсі, що дизайнери мов не прагнули продумати кожен оператор, який вони могли б додати до мови, тому менше додаткових операторів простіше.


3

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

Замість того, щоб писати

foreach(item in coll)
{
   bVal = bVal || fn(item); // not so elegant
}

Я хочу писати

foreach(item in coll)
{
  bVal ||= fn(item);    // elegant
}

і знайте, що після того, як bVal істинно, fn () не буде викликатися протягом решти ітерацій.


1
Для цього циклу ви, мабуть, просто хочете зробити if (fn(item)) { bVal = true; break; }.
Radiodef

2

' &' та ' &&' - не те саме, що ' &&' - це операція швидкого скорочення, яка не буде виконуватися, якщо перший операнд хибний, тоді як ' &' все одно зробить це (працює як з числом, так і з логічним значенням).

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

Дійсно не можу придумати, чому.


0

Це дозволено в Ruby.

Якби я здогадався, я б сказав, що він не часто використовується, тому не був реалізований. Іншим поясненням може бути те, що синтаксичний аналізатор дивиться на символ лише перед символом =


1
Java підтримує << =, >> = і >>> =, тому це не є суворою правдою.
Yishai

правда. Я про них не думав. Я думаю, єдине пояснення полягає в тому, як часто воно використовується.
zzawaideh,

0

Я не можу придумати кращої причини, ніж тоді: "Це виглядає неймовірно потворно!"


9
Але тоді C / Java ніколи не передбачалася бути красивою.
EFraim

1
Навряд чи я вважав &&=би гидким
запитання

0

&

перевіряє обидва операнди, це побітовий оператор. Java визначає кілька побітових операторів, які можна застосувати до цілочисельних типів, long, int, short, char та byte.

&&

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

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

Наявність оператора присвоєння && = насправді не додасть нової функціональності мові. Порозрядна арифметика оператора набагато виразніша, ви можете робити цілу розрядну арифметику, яка включає в себе логічну арифметику. Логічні оператори можуть просто виконувати булеву арифметику.


0

Брайан Гетц (архітектор мови Java в Oracle) писав :

https://stackoverflow.com/q/2324549/ [це питання] показує, що існує зацікавленість у наявності цих операторів, і немає чітких аргументів, чому вони ще не існують. Отже, питання полягає в тому: чи обговорювала команда JDK раніше додавання цих операторів, і якщо так, то де причини проти їх додавання?

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

"Вага його ваги" повинна оцінюватися за її витратами та вигодами, а також за співвідношенням витрат і вигод щодо інших характеристик кандидата.

Думаю, ви неявно припускаєте (за фразою "є інтерес"), що вартість майже нульова, а вигода більша за нуль, тому видається очевидним виграшем. Але це заперечує неправильне розуміння вартості; така функція впливає на специфікацію мови, реалізацію, JCK та кожну IDE та підручник Java. Немає тривіальних мовних особливостей. І користь, хоча і ненульова, є досить невеликою.

По-друге, ми можемо робити нескінченно багато функцій , але ми маємо можливість робити кілька разів кожні кілька років (а користувачі мають обмежену здатність поглинати нові функції.) Тому ми повинні бути дуже обережними щодо того, який саме вибір ми вибираємо, оскільки кожна функція (навіть банальна, здавалося б) споживає частину цього бюджету і незмінно забирає його у інших.
Це не "чому не ця функція", а "які ще функції ми не будемо робити (або затримувати), щоб ми могли зробити цю, і чи є це хорошим обміном?" І я не можу собі уявити, що це хороша торгівля з чим-небудь ще, над чим ми працюємо.

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


-1

a & b та a&& b - це не одне і те ж.

a && b - це булевий вираз, який повертає булеве значення, а a & b - побітове вираження, яке повертає int (якщо a та b є ints).

як ви думаєте, що вони однакові?


1
a & b повинен на відміну від a&& b завжди оцінювати b.
Даніель Брюкнер,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.