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


82

因此,对于在布尔二元运算符,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仅被评估一次。

为了证明这一点,以下代码段抛出NullPointerException,而不是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:这个问题比较老,但这有更多的选票和传入链接。我想说如果有任何合并,请将旧的合并到此(是的,可以做到)。
polygenelubricants

Answers:


27

原因

运算符&&=||=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专家Yannick进行了核对。我已使用用于检查这一点的Java代码源更新了答案。如您所说,&&&给出相同的结果。非常感谢您的反馈。你喜欢我的答案吗?干杯。
oHo,2012年

2
如果我想很快做该怎么办?&& =会比&=(如果存在)快,所以您应该if (a) a = b提高速度
AdventurerOK

嗨@adventurerOK。抱歉,我不确定您的意思...我认为a&=b;if(a) a=b;使用CPU寄存器中存储的值更快。但是,如果b位于外部存储器中(未缓存),则if(a) a=b;速度更快。是你的意思吗?请提供更多示例代码;-)我很好奇您的意见。再见。干杯
oHo,2012年

12
当您说“这不是我们想要的”时,我不同意。如果我写的话,isOK &&= f2();我希望它像短路一样短路&&
Tor Klingberg 2013年

3
我不同意您关于操作员容易出错或无用的说法。使用复合分配是通常的捷径A = A op B,因此每个人都完全知道自己在做什么,并且可以自己关心所涉及的问题。如果确实是您缺席的原因,那么我会认为它是不必要的光顾。但是,我要感谢您添加这一行,bool isOk = f1() || f2() || f3() || f4();因为那是我盲目见到自己的目的。
Franz B.

16

可能是因为

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

看起来应该分配x和评估someComplexExpression(),但是评估取决于评估的价值。x从语法上看,求并不明显。

同样由于Java的语法基于C,并且没有人迫切需要添加这些运算符。无论如何,使用if语句可能会更好。


37
我觉得这不是一个很好的答案,因为人们可以争辩说,x() && y() 看起来像它应该计算表达式的两侧。显然,人们接受&&短路,因此也应遵循&&=
polygenelubricants

2
@jleedev,同意。我相信在这些情况下,请务必记住这与x = x && someComplexExpression()等效,但与x = someComplexExpression()&& x等效。应当首先评估右侧,以使其与其他所有赋值运算符一致。考虑到这一点,&& =将与&=没有不同的行为。
PSpeed 2010年

@polygene公平。外观看起来是基于您熟悉的内容,我想他们不想添加任何新内容。我喜欢C / C ++ / Java / C#的一件事是表达式的基本语法几乎相同。
Josh Lee 2010年

1
@PSpeed,您弄错了。JLS非常清楚复合任务应该做什么。请参阅上面的内容。
polygenelubricants 2010年

1
@PSpeed:在这种情况下,其a -= b;工作方式与完全不同a &&= b;
David Thornley 2010年

10

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

现在,为什么在C中如此的问题是因为当&和&&成为不同的运算符时(有时在C从B下降之前),&=多种运算符被忽略了。

但是我的答案的第二部分没有任何来源可以支持它。


1
有趣的是-最近在C论坛上提出了这个问题;并且此答案已链接(尽管未标记为重复),从而完成了循环参数!
UKMonkey

5

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

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

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


我相信您是说条件运算符的对等物之间没有明显的对应关系。在JLS术语中,布尔逻辑运算符是&|^; &&并且||是条件运算符。
polygenelubricants

在C术语中,&&和|| 是“逻辑运算符”,ISO 9899:1999中的s6.5.13-14。当应用于单个位时,按位运算符只是“逻辑”(java中的布尔值);C中没有单一位类型,并且那里的逻辑运算符适用于所有标量类型。
p00ya 2010年

在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为true,就不会在其余的迭代中调用fn()。


1
对于该循环,您可能只想这样做if (fn(item)) { bVal = true; break; }
Radiodef 2015年

2

&”和“ &&”与“ &&”是一种捷径操作,如果第一个操作数为假,则&不会执行,而“ ”仍会执行此操作(同时使用number和boolean)。

我确实同意存在的意义更大,但是如果不存在的话也并不坏。我猜它不存在,因为C没有它。

真的想不出原因。


0

在Ruby中允许。

如果我猜到了,我会说它不经常使用,因此没有实现。另一个解释可能是解析器仅查看=之前的字符。


1
Java支持<< =,>> =和>>> =,因此严格来说并非如此。
Yishai

真正。我没想到这些。我猜唯一的解释就是使用的频率。
zzawaideh,2009年


0

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

&&

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

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

拥有&& =赋值运算符并不会真正为该语言添加新功能。按位运算符的算术表达性更高,可以执行整数按位算术,其中包括布尔算术。逻辑运算符只能执行布尔运算。


0

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

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

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

需要根据其成本和收益以及相对于其他候选功能的成本效益比来判断“担负重”。

我认为您是在隐式假设(用短语“有利息”表示)成本接近零且收益大于零,因此这似乎是一个明显的胜利。但这掩盖了对成本的错误理解。这样的功能会影响语言规范,实现,JCK以及每个IDE和Java教科书。没有琐碎的语言功能。而且,收益虽然非零,却很小。

其次,我们可以无限地完成许多功能,但是每隔几年我们只能做少数几个功能(并且用户吸收新功能的能力有限。)因此我们在选择时必须非常小心,因为每个功能(甚至是微不足道的功能)都会消耗一部分预算,并且总是将其从其他预算中夺走。
这不是“为什么没有此功能”,而是“我们将不做(或延迟)其他什么功能,以便我们可以做到这一点,这是一笔好买卖吗?” 我真的无法想象这与我们正在努力的任何其他事物之间都是一笔不错的交易。

因此,它清除了“一个绝妙的主意”的标准(这已经相当不错了,很多功能请求甚至都没有清除它),但是似乎不太可能清除“更好地利用我们的发展预算”的标准。比什么都重要。”


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.