Почему в Java нет составных версий присваивания для операторов условного и и условного или? (&& =, || =)


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, а не ArrayIndexOutOfBoundsException.

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

2
Я выбираю второе, никого не волнует: P также, все эти вопросы о том, «почему функция x отсутствует на языке y?» следует спросить у разработчиков языка, а не у нас: P
Федерико Клез Каллока 01

1
Что означает & =? Кто-нибудь может мне сказать?
Tarik

@ Аарон: а = а и б. Это написано в вопросе
Федерико Клез Каллока 01


1
@jleedev: Этот вопрос старше, но у него больше голосов и входящих ссылок. Я бы сказал, что если есть какое-то слияние, слейте старый с этим (да, это можно сделать).
polygenelubricants

Ответы:


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();where f2()вызывается только при f1()возврате true.

Если разработчик хочет, f2()чтобы его вызывали только после f1()возврата true, второй код выше менее подвержен ошибкам.

Else &=достаточно, потому что разработчик хочет, 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 03

2
Что, если я захочу сделать это очень быстро? && = был бы быстрее, чем & =, если бы он существовал, поэтому вы должны использовать его if (a) a = bдля скорости
adventurerOK

Привет, @adventurerOK. Извините, я не уверен, что понимаю, что вы имеете в виду ... Я думаю, a&=b;это быстрее, чем if(a) a=b;при использовании значений, хранящихся в регистрах процессора. Однако, если он bнаходится во внешней памяти (не кэширован), то if(a) a=b;быстрее. Вы это имеете в виду? Пожалуйста, предоставьте больше примеров кода ;-) Мне интересно ваше мнение. Увидимся. Cheers
oHo

13
Я не согласен, когда вы говорите: «А это не то, что мы хотим». Если я напишу, isOK &&= f2();я бы хотел, чтобы он замкнулся, как и &&делает.
Tor Klingberg

4
Я не согласен с вашим утверждением, что операторы будут подвержены ошибкам или бесполезны. Использование составных присваиваний - это то, что вы делаете, чтобы сократить путь к обычному A = A op B, чтобы все прекрасно знали, что они делают, и могли сами позаботиться о последствиях. Если бы ваши причины действительно были причиной его отсутствия, я бы воспринял это как нежелательное покровительство. Однако я хочу поблагодарить вас за добавление строки, bool isOk = f1() || f2() || f3() || f4();потому что это то, что я должен был ослепить, чтобы увидеть себя.
Franz B.

17

Наверное, потому что что-то вроде

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

похоже, что это должно быть присвоение xи оценка someComplexExpression(), но тот факт, что оценка зависит от значенияx не очевиден из синтаксиса.

Также потому, что синтаксис Java основан на C, и никто не видел острой необходимости добавлять эти операторы. В любом случае вам, вероятно, будет лучше с оператором if.


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

2
@jleedev, согласился. Я считаю, что в таких ситуациях важно помнить, что это не эквивалент x = x && someComplexExpression (), а эквивалент x = someComplexExpression () && x. Правая часть будет / должна быть оценена в первую очередь, чтобы быть совместимой с любым другим оператором присваивания. И при этом поведение && = не отличается от поведения & =.
PSpeed

@polygene Это честно. Однако то, как что-то выглядит, зависит от того, с чем вы знакомы, и я думаю, они не хотели добавлять ничего нового. Что мне нравится в C / C ++ / Java / C #, так это то, что основной синтаксис выражений практически идентичен.
Джош Ли,

1
@PSpeed, вы ошибаетесь. JLS очень четко определяет, что должно делать составное присваивание. Смотрите мое добавление выше.
polygenelubricants

1
@PSpeed: в этом случае a -= b;будет работать совершенно иначе, чем a &&= b;.
Дэвид Торнли,

11

Так в Java, потому что так в C.

Теперь вопрос, почему это так в C, заключается в том, что, когда & и && стали разными операторами (когда-то предшествовавшим происхождению C от B), разнообразие операторов & = было просто упущено.

Но у второй части моего ответа нет источников, подтверждающих это.


1
Что забавно - вопрос недавно был задан на форуме C; и этот ответ был связан (хотя и не отмечен как дубликат), что завершает круговой аргумент!
UKMonkey

6

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

Интересно, что в таких языках, как ruby , есть || = и && =.

Изменить: терминология отличается между Java и C


Я полагаю, вы имеете в виду, что условные эквиваленты операторов не имеют такого очевидного соответствия. В терминологии JLS булевыми логическими операторами являются &, |и ^; &&и ||являются условными операторами.
polygenelubricants

В терминологии C && и || являются «логическими операторами», s6.5.13-14 в ISO 9899: 1999. Побитовые операторы являются «логическими» только тогда, когда они применяются к одному биту (логическое значение в java); в C нет однобитового типа, и там логические операторы применяются ко всем скалярным типам.
p00ya

до C99 у C даже не было boolтипа. Это 20 лет в истории языка без bool. Для этого не было причин &&=. побитовых операций было достаточно.
v.oddou 02 апр.'20

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

' &' и ' &&' - это не то же самое, что ' &&' - это сокращенная операция, которая не будет работать, если первый операнд ложный, в то время как ' &' будет делать это в любом случае (работает как с числом, так и с логическим).

Я согласен с тем, что в существовании больше смысла, но не так уж и плохо, если его нет. Думаю, его там не было, потому что в C его нет.

На самом деле не могу понять почему.


0

Это разрешено в Ruby.

Если бы я догадывался, я бы сказал, что он не часто используется, поэтому не был реализован. Другое объяснение может заключаться в том, что парсер смотрит только на символ перед =


1
Java поддерживает << =, >> = и >>> =, так что это не совсем так.
Ишай,

правда. Я не думал об этом. Думаю, единственное объяснение - это то, как часто он используется.
zzawaideh 05

0

Я не могу придумать лучшей причины, чем «Это выглядит невероятно уродливо!»


9
Но тогда C / Java никогда не был красивым.
EFraim 01

1
Я бы не считал &&=себя некрасивым
asgs

0

&

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

&&

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

Оператор && похож на оператор &, но может сделать ваш код более эффективным. Поскольку оба выражения, сравниваемые оператором &, должны быть истинными, чтобы все выражение было истинным, нет причин оценивать второе выражение, если первое возвращает ложь. Оператор & всегда вычисляет оба выражения. Оператор && вычисляет второе выражение, только если первое выражение истинно.

Наличие оператора присваивания && = не добавит языку новых функциональных возможностей. Арифметика побитового оператора намного более выразительна, вы можете выполнять целочисленную побитовую арифметику, в том числе логическую арифметику. Логические операторы могут просто выполнять логическую арифметику.


0

Брайан Гетц (архитектор языка Java в Oracle) написал :

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

Мне не известно о каком-либо конкретном обсуждении этого конкретного вопроса, но если бы кто-то его предложил, ответ, скорее всего, был бы таков: это не безосновательный запрос, но он не имеет веса.

«Несущий вес» необходимо оценивать по его затратам и выгодам, а также по соотношению затрат и выгод по сравнению с другими возможными функциями.

Я думаю, вы неявно предполагаете (с помощью фразы «есть интерес»), что стоимость близка к нулю, а выгода больше нуля, так что это кажется очевидным выигрышем. Но это противоречит неправильному пониманию стоимости; Подобная функция влияет на спецификацию языка, реализацию, JCK и все учебники по IDE и Java. Нет тривиальных языковых функций. И выгода, хотя и отличная от нуля, довольно мала.

Во-вторых, есть бесконечно много функций, которые мы могли бы реализовать , но у нас есть возможность делать лишь несколько функций каждые несколько лет (а у пользователей есть ограниченные возможности для освоения новых функций). Поэтому мы должны быть очень осторожны с тем, что мы выбираем, поскольку каждая функция (даже кажущаяся тривиальной) потребляет часть этого бюджета и неизменно отнимает ее у других.
Это не «почему не эта функция», а «какие еще функции мы не будем делать (или откладывать), чтобы мы могли сделать это, и это хорошая сделка?» И я не могу себе представить, чтобы это была хорошая сделка против всего, над чем мы работаем.

Таким образом, он очищает планку от «неплохой идеи» (что уже довольно хорошо, многие запросы функций даже не очищают этого), но вряд ли когда-либо очистит планку от «более эффективного использования нашего бюджета развития» чем что-либо еще ".


-1

a & b и a && b - это не одно и то же.

a && b - это логическое выражение, которое возвращает логическое значение, а a & b - побитовое выражение, возвращающее int (если a и b - это целые числа).

как вы думаете, они такие же?


1
a & b, в отличие от a && b, всегда должен оценивать b.
Даниэль Брюкнер,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.