87

所以对于布尔值的二元运算符,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];
4

11 回答 11

30

原因

运算符&&=和在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();wheref2()仅在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 ...
于 2012-03-29T15:30:09.490 回答
17

可能是因为类似

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

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

还因为 Java 的语法是基于 C 的,没有人认为迫切需要添加这些运算符。无论如何,使用 if 语句可能会更好。

于 2010-02-24T08:33:31.823 回答
12

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

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

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

于 2009-10-01T17:39:22.773 回答
7

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

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

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

于 2010-02-24T08:31:06.897 回答
4

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

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

于 2009-10-01T17:59:54.783 回答
3

对于布尔变量,&& 和 || 将使用短路评估而 & 和 | 不要,所以你会期望 &&= 和 ||= 也使用短路评估。有一个很好的用例。尤其是当您在循环中进行迭代时,您希望快速、高效且简洁。

而不是写

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

我想写

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

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

于 2013-02-21T16:03:56.583 回答
2

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

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

实在想不通为什么。

于 2009-10-01T17:50:24.123 回答
1

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

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

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

“负重前行”需要根据其成本和收益,以及相对于其他候选特征的成本收益比来判断。

我认为您隐含地假设(通过“有兴趣”这一短语)成本接近于零,而收益大于零,因此这似乎是一个明显的胜利。但这掩盖了对成本的错误理解;像这样的特性会影响语言规范、实现、JCK 以及所有 IDE 和 Java 教科书。没有微不足道的语言特征。好处虽然不为零,但非常小。

其次,我们可以做无限多的功能,但我们每隔几年只能做少数几个(而且用户吸收新功能的能力有限。)所以我们必须非常小心选择哪个,因为每个功能(即使是看似微不足道的功能)都会消耗一些预算,并且总是会从其他功能中拿走它。
这不是“为什么不做这个功能”,而是“我们不会做(或延迟)哪些其他功能,以便我们可以做这个,这是一笔好的交易吗?” 而且我真的无法想象这对我们正在做的任何其他事情来说是一笔不错的交易。

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

于 2020-08-16T14:08:40.777 回答
0

在 Ruby 中是允许的。

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

于 2009-10-01T17:39:55.377 回答
0

我想不出比“它看起来难以置信的丑陋!”更好的理由。

于 2009-10-01T17:40:14.080 回答
0

&

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

&&

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

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

拥有 &&= 赋值运算符并不会真正为语言添加新功能。位运算符的运算更具表现力,您可以进行整数位运算,其中包括布尔运算。逻辑运算符只能进行布尔运算。

于 2015-06-12T13:20:56.353 回答