1. 概述

Java提供了一套位运算符。这些运算符使我们能够方便地操作数字的单个位。

然而,在比较位运算的结果时,我们可能会遇到一个常见的陷阱。

在这个简短的教程中,我们将讨论为什么我们可能会遇到Java编译时错误“二元运算符的无效操作数类型”,以及如何解决这个问题。

2. 问题介绍

如常,我们将通过一个例子来理解这个问题。但首先,让我们看看一个简单的方法:

public void checkNumber() {
    List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    intList.forEach(i -> {
        if (i & 1 == 1) {
            System.out.println(i + " is odd.");
        } else {
            System.out.println(i + " is even.");
        }
    });
}

我们可以看到,checkNumber方法遍历intList,检查并输出每个数字是奇数还是偶数。

需要注意的是,方法中的奇数检查逻辑并没有使用常见的方式:i % 2 == 1。相反,我们在Integer类型的数i上执行位与(&)操作。如果结果是1,我们知道整数i是奇数:i & 1 == 1

然而,当我们尝试测试上面的方法时,代码出乎意料地无法编译:

java: bad operand types for binary operator '&'
  first type:  java.lang.Integer
  second type: boolean

接下来,让我们理解问题的原因并学习如何解决。

3. 理解Java运算符优先级

首先,错误消息相当直接。它指出我们试图对boolean类型和Integer类型进行位与运算。

然而,这很奇怪,因为我们确实写了"i & 1"在代码中。为什么编译器认为boolean类型参与了位与运算?

这是因为“*==”运算符的优先级高于“&”运算符。也就是说,“i & 1 == 1”等同于“i & (1 == 1)”。因此,我们有“i & true (boolean)*”。

现在,我们可能问:“好吧,==的优先级高于*&*。但是为什么i % 2 == 1能得到预期的结果呢?”

为了回答这个问题,我们需要更深入地了解Java运算符的优先级规则。

Java提供了许多运算符。在实践中,我们经常一起使用不同的运算符。因此,理解Java运算符的优先级至关重要。否则,我们可能会得到意想不到的结果。

接下来,让我们看看Java运算符的优先级规则(表中的运算符越靠前,其优先级越高):

运算符

优先级

后缀

expr++ expr--

一元

++expr –expr +expr -expr ~ !

乘法

* / %

加法

+ –

移位

<< >> >>>

关系

< > <= >= instanceof

相等

== !=

位与

&

异或

^

与或

|

逻辑与

&&

逻辑或

||

三元

? :

赋值

= += -= *= /= %= &= ^= |= <<= >>= >>>=

如列表所示,*取模运算符()的优先级高于相等运算符(==)。另一方面,位与运算符(&)在表中的优先级低于相等运算符(==)**。

这就是为什么i % 2 == 1能得到预期结果,而i & 1 == 1却不能。

我们的示例中遇到了编译时错误。所以,我们可以在早期阶段检测到问题。但是,想象一下,如果带有运算符优先级bug的实现编译通过,但产生了错误的结果,找到问题的真实原因可能会无谓地花费大量时间。

因此,记住Java运算符的优先级规则是值得的。

4. 解决问题

现在我们理解了问题的原因,解决问题并不困难。我们只需要在位与运算上添加括号:

if (i & 1 == 1)  -->  if ((i & 1) == 1)

修复后,如果我们再次运行方法,我们会发现编译器不再抱怨,并收到预期的输出:

1 is odd.
2 is even.
3 is odd.
4 is even.
5 is odd.
6 is even.
7 is odd.

5. 总结

在这篇简短的文章中,我们通过一个位与运算的例子分析了“二元运算符的无效操作数类型”的编译错误。

此外,我们讨论了Java运算符的优先级规则。

最后,我们解决了问题。