Why does an expression like flags & MASK == 0 often behave unexpectedly in Java, and how does the precedence table explain it?
answer
- == ranks higher than & in Java
- flags & MASK == 0 -> flags & (MASK == 0)
- int & boolean is a compile error in Java
- Always parenthesize bitwise vs comparison
- Legacy from C's precedence table
basics
~20 sBecause == has higher precedence than &, Java reads it as flags & (MASK == 0), not (flags & MASK) == 0. The comparison runs first, which is almost never what you meant. Add parentheses: (flags & MASK) == 0.
solid answer
~50 sThis is a classic precedence trap. In Java the bitwise operators `&`, `^`, `|` sit *below* the equality operators `==`/`!=` in the precedence table — a legacy of C. So `flags & MASK == 0` parses as `flags & (MASK == 0)`. That tries to bitwise-AND an `int` with a `boolean`, which fails to compile in Java (in C it silently compiled to nonsense). Even `flags & MASK != 0` parses as `flags & (MASK != 0)` and also won't compile against an int mask. The fix is explicit parentheses: `(flags & MASK) == 0` or `(flags & MASK) != 0`. The deeper lesson: whenever you mix bitwise operators with relational/equality operators, always parenthesize the bitwise part. Many style guides and static-analysis rules flag unparenthesized bitwise-in-comparison precisely because the intuitive reading and the actual grouping diverge.
code
java · 9 linesint flags = 0b1010, MASK = 0b0010;
// Misgrouped: flags & (MASK == 0) -> int & boolean -> COMPILE ERROR
// if (flags & MASK == 0) { ... }
// Correct: mask first, then compare
if ((flags & MASK) != 0) {
System.out.println("bit is set");
}go deeper
May not hit this often; should recognize the fix is to add parentheses around the masking.
Knows == outranks & in Java, can explain why the unparenthesized form misgroups, and writes (flags & MASK) == 0 by habit.
Explains the C heritage, the Java type-error safety net versus C's silent bug, and recommends linter rules to catch it.
Establishes team conventions and static-analysis gates for bitwise/comparison mixing and reasons about how legacy precedence choices propagate footguns across languages.
## The surprising ranking Intuitively you might expect `&` (bitwise AND) to bind tighter than `==` (equality), because masking 'feels' like part of building the value you then compare. But Java inherited C's precedence table, where the **equality** operators `==`/`!=` rank **higher** than the **bitwise** operators `&`, `^`, `|`. Concretely, from higher to lower: relational -> equality -> `&` -> `^` -> `|` -> `&&` -> `||`. ## What that does to flags & MASK == 0 Because `==` outranks `&`, the compiler groups the comparison first: ``` flags & MASK == 0 parses as flags & (MASK == 0) ``` Now `MASK == 0` is a **boolean**, and `flags & boolean` mixes an `int` with a `boolean`. In Java that is a **compile error** (`bad operand types for binary operator '&'`), which at least catches the mistake. In C the same text compiled silently (booleans were ints), producing wrong runtime results — the historical reason this trap is infamous. ## The correct form ```java if ((flags & MASK) == 0) { ... } // mask first, then compare if ((flags & MASK) != 0) { ... } // 'is this bit set?' ``` The parentheses force the AND to happen first, giving the int result you then compare to 0. ## Why the language keeps this ranking It is a compatibility decision: keeping C's table makes C/C++ code visually translate to Java and avoids surprising C programmers. The downside is exactly this footgun, so the practical rule is cultural, not structural. ## The general rule Whenever you combine **bitwise** operators (`&`, `|`, `^`) with **comparison** operators (`==`, `!=`, `<`, `>`, ...), **always wrap the bitwise sub-expression in parentheses**. The same applies when mixing `&&`/`||` with assignment or ternary. Treat the precedence table as something to *defend against* with parentheses rather than rely on from memory. Linters (Checkstyle, SpotBugs, IntelliJ inspections) have dedicated rules for unparenthesized bitwise-in-comparison. ## Deriving the answer Look up the two operators in the table: `==` is higher than `&`. Higher binds first, so the comparison groups before the AND. Recognize the resulting type mismatch (int & boolean) and fix with parentheses.
- Why does flags & (MASK == 0) fail to compile in Java but 'worked' in C?In Java, & between an int and a boolean is a type error because boolean is a distinct type. In C, booleans were just ints, so int & int compiled and silently produced a wrong result — which is why the trap is more dangerous in C.
- What is the safe habit when combining bitwise and relational operators?Always parenthesize the bitwise sub-expression, e.g. (flags & MASK) != 0, so grouping matches intent and does not depend on remembering that == outranks &.
saying these in an interview costs you the question
- Assuming & binds tighter than ==
- Thinking flags & MASK == 0 means (flags & MASK) == 0 without parentheses
- Believing it compiles and just returns the wrong value (in Java it is a type error)
- Mixing bitwise and comparison operators without parentheses in production code