为什么 Java 没有条件和条件或运算符的复合赋值版本?(&&=, ||=)

Why doesn't Java have compound assignment versions of the conditional-and and conditional-or operators? (&&=, ||=)

提问人:polygenelubricants 提问时间:2/24/2010 最后编辑:polygenelubricants 更新时间:9/2/2022 访问量:19869

问:

因此,对于布尔值上的二进制运算符,Java 有 、 、 和 。&|^&&||

让我们在这里简要总结一下他们的工作:

对于 ,如果两个操作数值均为 ;否则,结果为 。&truetruefalse

对于 ,如果两个操作数值均为 ;否则,结果为 。|falsefalsetrue

对于 ,如果操作数值不同,则结果值为;否则,结果为 。^truefalse

运算符类似于,但仅当其左手操作数的值为 时才计算其右操作数。&&&true

运算符类似于 ,但仅当其左操作数的值为 时,才计算其右操作数。|||false

现在,在所有 5 个中,其中 3 个具有复合赋值版本,即 和 。所以我的问题很明显:为什么 Java 不提供 and?我发现我需要的比我需要的还要多。|=&=^=&&=||=&=|=

而且我不认为“因为它太长”是一个好的答案,因为 Java 有.这种遗漏一定有更好的理由。>>>=


15.26 开始,赋值运算符

有 12 个赋值运算符;[...]= *= /= %= += -= <<= >>= >>>= &= ^= |=


有人评论说,如果实施了,那么它将是唯一不首先评估右侧的操作员。我认为复合赋值运算符首先计算右侧的想法是错误的。&&=||=

15.26.2 开始,复合赋值运算符

形式的复合赋值表达式等价于 ,其中 是 的类型,只不过只计算一次。E1 op= E2E1 = (T)((E1) op (E2))TE1E1

作为证明,以下代码片段抛出 ,而不是 .NullPointerExceptionArrayIndexOutOfBoundsException

    int[] a = null;
    int[] b = {};
    a[0] += b[-1];
java 运算符 短路复合 赋值

评论

2赞 Federico klez Culloca 10/2/2009
我选择第二个,没有人在乎:P此外,所有这些关于“为什么特征X不在语言Y中?”的问题都应该问语言的设计者,而不是我们:P
1赞 Tarik 10/2/2009
&= 是什么意思?有人可以告诉我吗?
0赞 Federico klez Culloca 10/2/2009
@Aaron: a = a & b.它写在问题中
1赞 Josh Lee 8/16/2010
为什么“&&=”运算符不存在?
1赞 polygenelubricants 8/16/2010
@jleedev:这个问题比较老,但有更多的选票和传入链接。我会说,如果有任何合并,请将旧的合并到这个合并中(是的,可以做到)。

答:

12赞 EFraim 10/2/2009 #1

在 Java 中是这样,因为在 C 中也是这样。

现在的问题是,为什么在C中会这样,因为当&和&&成为不同的运算符时(在C从B下降之前的某个时间),&=运算符的多样性被忽略了。

但我回答的第二部分没有任何来源来支持它。

评论

2赞 UKMonkey 3/13/2019
有趣的是 - 这个问题最近在 C 论坛上被问到;并且这个答案是链接的(虽然没有标记为重复)这使得循环论证完成!
0赞 zzawaideh 10/2/2009 #2

这在 Ruby 中是允许的。

如果我猜的话,我会说它不经常使用,所以它没有实现。另一种解释可能是解析器只查看 = 之前的字符

评论

1赞 Yishai 10/2/2009
Java 支持 <<=、>>= 和 >>>=,所以这并不完全正确。
0赞 zzawaideh 10/5/2009
真。我没有想到这些。我想唯一的解释是它的使用频率。
0赞 Daniel Brückner 10/2/2009 #3

我想不出更好的理由了,'它看起来非常丑陋!

评论

10赞 EFraim 10/2/2009
但那时 C/Java 从来都不是美的。
2赞 asgs 5/17/2017
我几乎不认为自己丑陋&&=
2赞 NawaMan 10/2/2009 #4

'&' 和 '' 不一样,因为 '' 是一个快捷方式操作,如果第一个操作数是假的,则不会这样做,而 '' 无论如何都会这样做(适用于数字和布尔值)。&&&&&

我确实同意存在更有意义,但如果它不存在,它并没有那么糟糕。我想它不存在,因为 C 没有它。

真的想不出为什么。

4赞 Yishai 10/2/2009 #5

Java 的最初目标之一是“简单、面向对象和熟悉”。在这种情况下,&= 是熟悉的(C,C++ 有它,在这种情况下,熟悉意味着知道这两者的人熟悉)。

&&= 不会很熟悉,也不会很简单,因为语言设计者不会考虑他们可以添加到语言中的每个运算符,所以更少的额外运算符更简单。

7赞 p00ya 2/24/2010 #6

很大程度上是因为 Java 语法是基于 C(或至少是 C 系列)的,而在 C 中,所有这些赋值运算符都会被编译为单个寄存器上的算术或按位汇编指令。赋值运算符版本避免了临时性,并且可能在早期的非优化编译器上生成了更有效的代码。逻辑运算符(在 C 中称为)等价物 ( 和 ) 与单个汇编指令没有如此明显的对应关系;它们通常扩展到测试和分支指令序列。&&=||=

有趣的是,像 ruby 这样的语言确实有 ||= 和 &&=。

编辑:Java和C之间的术语不同

评论

0赞 polygenelubricants 2/24/2010
我相信你的意思是条件运算符等价物没有如此明显的对应关系。在 JLS 术语中,布尔逻辑运算符是 和 ; 和 是条件运算符。&|^&&||
0赞 p00ya 2/24/2010
在 C 术语中,&& 和 ||是“逻辑运算符”,ISO 9899:1999 中的 s6.5.13-14。按位运算符仅在应用于单个位(java 中的布尔值)时才是“逻辑”的;C 中没有单位类型,那里的逻辑运算符适用于所有标量类型。
0赞 v.oddou 4/2/2020
在 C99 之前,C 甚至没有类型。这是语言史上没有布尔值的 20 年。没有理由.按位运算就足够了。bool&&=
0赞 Elmar Zander 10/15/2021
+1 唯一真正有意义的答案。没有比 Java 的 C 传统更深层次的原因了。C语言过去(现在仍然很大程度上)只是一个“高级汇编器”,从这个角度来看,不需要这些运算符,因为它们不会编译成更有效的机器代码。
17赞 Josh Lee 2/24/2010 #7

可能是因为类似的东西

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

看起来它应该分配给和评估,但从语法中看不出评估取决于 的值这一事实并不明显。xsomeComplexExpression()x

此外,因为 Java 的语法是基于 C 语言的,没有人认为迫切需要添加这些运算符。无论如何,你最好使用 if 语句。

评论

43赞 polygenelubricants 2/24/2010
我认为这不是一个好的答案,因为有人可能会争辩说,看起来它应该评估表达的两面。显然,人们接受这是短路,所以也应该遵循。x() && y()&&&&=
2赞 PSpeed 2/24/2010
@jleedev,同意了。我相信在这些情况下,重要的是要记住,这并不等同于 x = x && someComplexExpression(),而是等同于 x = someComplexExpression() && x。将/应首先评估右侧,以使其与其他所有赋值运算符保持一致。鉴于此,&&= 的行为与 &= 没有不同。
2赞 polygenelubricants 2/24/2010
@PSpeed,你错了。JLS非常清楚复合分配应该做什么。请参阅我上面的补充。
1赞 polygenelubricants 2/24/2010
我认为说它们不存在是因为设计者只是遵循了 C 设置的优先级是误导性的,因为他们确实添加了 和 ,这是全新的。>>>>>>=
2赞 David Thornley 3/22/2010
@PSpeed:在这种情况下,工作方式与 .a -= b;a &&= b;
33赞 oHo 3/29/2012 #8

原因

运算符 和 在 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

这第一个代码很容易出错,因为许多开发人员会认为无论 f1() 返回值如何,它总是被调用。这就像只有在返回时才调用 where。f2()bool isOk = f1() && f2();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赞 oHo 4/3/2012
嗨,@StriplingWarrior。我已经咨询了我的同事 Yannick,我们最好的 Java 专家。我已经使用用于检查该点的 Java 代码源代码更新了我的答案。正如你所说,并给出相同的结果。非常感谢您的反馈。你喜欢我的回答吗?干杯。&&&
2赞 adventurerOK 6/19/2012
如果我想非常快地做到这一点怎么办?&&= 会比 &= 快,如果存在,所以你应该使用 for speedif (a) a = b
0赞 oHo 6/25/2012
嗨,@adventurerOK。对不起,我不确定你的意思......我认为比使用存储在 CPU 寄存器中的值更快。但是,如果在外部存储器中(未缓存),则速度更快。是你的意思吗?请提供更多示例代码;-)我很好奇你的意见。再见。干杯a&=b;if(a) a=b;bif(a) a=b;
17赞 Tor Klingberg 6/4/2013
当你说“这不是我们想要的”时,我不同意。如果我写,我希望它像做的那样短路。isOK &&= f2();&&
4赞 Franz B. 7/17/2015
我不同意你关于运算符容易出错或无用的说法。使用复合赋值是你为通常的捷径而做的事情,所以每个人都完全知道自己在做什么,并且可以自己关心其中的含义。如果你的理由确实是它缺席的原因,我会认为这是不必要的光顾。但是,我要感谢你添加这条线,因为这就是我要瞎眼才能看到自己的东西。A = A op Bbool isOk = f1() || f2() || f3() || f4();
3赞 zanfilip 2/22/2013 #9

对于布尔变量、&& 和 ||将使用 & 和 |不要,所以你会期望 &&= 和 ||= 也使用短路评估。这是一个很好的用例。特别是如果你在循环中迭代,你希望快速、高效和简洁。

而不是写作

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

我想写

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

并且知道一旦 bVal 为真,fn() 将不会在剩余的迭代中被调用。

评论

2赞 Radiodef 6/12/2015
对于该循环,您可能只想做 .if (fn(item)) { bVal = true; break; }
0赞 Ely 6/12/2015 #10

&

验证两个操作数,它是一个按位运算符。Java 定义了几个按位运算符,这些运算符可以应用于整数类型 long、int、short、char 和 byte。

&&

如果第一个操作数的计算结果为 false,则停止计算,因为结果将为 false,它是一个逻辑运算符。它可以应用于布尔值。

&&运算符类似于&运算符,但可以使你的代码更有效率。由于 & 运算符比较的两个表达式都必须为 true,整个表达式才能为 true,因此如果第一个表达式返回 false,则没有理由计算第二个表达式。& 运算符始终计算这两个表达式。&& 运算符仅在第一个表达式为 true 时计算第二个表达式。

使用 &&= 赋值运算符不会真正为语言添加新功能。按位运算符的算术更具表现力,可以做整数按位算术,其中包括布尔算术。逻辑运算符只能做布尔运算。

2赞 Marcono1234 8/16/2020 #11

Brian Goetz(Oracle 的 Java 语言架构师)写道

https://stackoverflow.com/q/2324549/[这个问题]表明有兴趣拥有这些运营商,并且那里 没有明确的论据说明为什么它们还不存在。 因此,问题是:JDK 团队过去是否讨论过添加这些运算符,如果是的话 反对添加它们的原因是什么?

我不知道关于这个特定问题的任何具体讨论,但是 如果有人提出它,答案可能是:它不是 不合理的要求,但它没有分量。

“负重其道理”需要根据其成本和收益来判断,并且 按其相对于其他候选特征的成本效益比。

我认为你是在含蓄地假设(通过“有兴趣”这句话) 成本接近于零,收益大于零,所以它 似乎是一个明显的胜利。但这掩盖了对 成本;像这样的功能会影响语言规范、实现、 JCK 以及所有 IDE 和 Java 教科书。没有琐碎的语言 特征。收益虽然不为零,但非常小。

其次,我们可以做无限多的功能,但是我们 每隔几年才有能力做一小撮(并且用户有一个 吸收新功能的能力有限。所以我们必须非常小心 至于我们选择哪个,因为每个功能(即使是看似微不足道的功能) 消耗了其中的一部分预算,并且总是从其他预算中拿走它。
这不是“为什么不做这个功能”,而是“我们不会做其他功能 (或延迟)这样我们就可以做这个,这是一笔好交易吗?而我 真的无法想象这是对我们其他任何东西的好交易 正在工作。

因此,它清除了“不是一个可怕的想法”(这已经很漂亮了)的障碍 很好,很多功能请求甚至没有清除这一点),但似乎 不太可能清除“更好地利用我们的进化预算”的障碍 比什么都重要。

0赞 Martin F Schulz 9/2/2022 #12

有趣的是,我遇到了这个问题。

运营商 ||= 和 &&= 不存在,因为它们的语义很容易被误解; 如果您认为需要它们,请改用 if-statement。

Ely(上面的帖子)说对了要点:

||

如果第一个操作数的计算结果为 true,则停止计算,因为结果为 true,它是一个逻辑运算符。

所以想象一下,如果 b == true,会发生什么;

乙 ||= somethingreturningaboolean();// !!??

如果 b == true,则不会调用 somethingreturningaboolean()。

这种行为在长形式中更为明显:

b = b ||somethingreturningaboolean();

这就是为什么 ||= 和 &&= 运算不存在。解释应如下: 运营商 ||= 和 &&= 不存在,因为它们的语义很容易被误解; 如果您认为需要它们,请改用 if-statement。