JAVA / VARIABLES, PRIMITIVES AND TYPES
boolean logic and where booleans are required
Write conditions Java accepts: combine comparisons with !, &&, || and ^, know why & differs from &&, and why an int can never stand in for a boolean.
What you will learn
- && and || skip their right operand when the answer is known; & and | never skip.
- Put null and bounds checks to the left of && so they actually protect the right side.
- Spot the if (done = true) typo: it compiles, always runs, and mutates done.
- Read a || b && c as a || (b && c), and know why (boolean) 1 is a compile error.
Understanding boolean logic and where booleans are required
Java's boolean is a type with exactly two values, true and false, and it has no numeric identity: 0 is not false, 1 is not true, and no cast converts between int and boolean. Every place that asks a yes/no question insists on that type: the condition of if, while and do/while, the middle clause of for, the test before ? in a ternary, and assert. This is stricter than C, and the payoff is that a mistyped if (count = 1) is a compile error instead of a silent bug. A local boolean must be assigned before it is read; a boolean field you never assign starts out false.
Conditions are not special syntax that only exists inside the parentheses after if. A comparison such as hits == 0, an instanceof test, or any method returning boolean produces an ordinary value you can store in a variable, pass as an argument, return, or print. Combine those values with ! for negation, && and || for and/or, and ^ for exclusive or, which is true exactly when its two operands differ. Naming a long test, as in boolean eligible = age >= 18 || hasGuardian;, costs nothing at runtime and usually reads better than inlining it.
The difference between && and & is when the operands are evaluated, not the answer: on two booleans both compute the same logical and, but && looks at its right operand only when the left is true, while & always evaluates both. That is why null and bounds checks belong on the left of &&; name != null && name.length() > 3 is safe, and swapping the halves, or writing & instead of &&, throws. There is no short-circuiting ^, because you cannot tell whether two values differ without looking at both. Precedence follows an and-before-or rule, much as * binds tighter than +, so a || b && c means a || (b && c).
public class BooleanBasics {
static int calls = 0;
static boolean check(boolean value) {
calls++;
return value;
}
public static void main(String[] args) {
boolean skipped = false && check(true);
System.out.println("after && : calls=" + calls + " result=" + skipped);
boolean forced = false & check(true);
System.out.println("after & : calls=" + calls + " result=" + forced);
String name = null;
if (name != null && name.length() > 3) {
System.out.println("long name");
} else {
System.out.println("no usable name");
}
int hits = 0;
System.out.println("hits == 0 is " + (hits == 0));
System.out.println("xor: " + (true ^ true) + " " + (true ^ false));
}
}A condition in Java is simply an expression whose type is boolean, and boolean is its own type that no number can substitute for.
Worked examples
The = that still compiles
Shows why the classic == typo is caught for ints but silently accepted for booleans.
public class AssignmentTrap {
public static void main(String[] args) {
boolean ready = false;
if (ready = true) {
System.out.println("branch taken, ready is now " + ready);
}
int count = 0;
// if (count = 1) { } // does not compile: int cannot be converted to boolean
if (count == 1) {
System.out.println("not printed");
}
System.out.println("count is still " + count);
}
}Example explained
Line 1if (ready = true): the assignment expression evaluates to the value assigned, true, so the condition holds and ready has been overwritten.
Line 2The commented if (count = 1) yields an int, which Java refuses where a boolean is required, so the identical typo is a compile error for numbers.
Line 3Writing if (ready) instead of if (ready == true) removes the chance of dropping one = in the first place.
Boolean versus boolean at an if
Demonstrates that a boxed Boolean is unboxed by the if itself, so a null flag throws there.
public class BoxedFlag {
public static void main(String[] args) {
Boolean flag = null;
System.out.println("flag == null is " + (flag == null));
try {
if (flag) {
System.out.println("not reached");
}
} catch (NullPointerException e) {
System.out.println("unboxing null threw NullPointerException");
}
System.out.println("Boolean.TRUE.equals(flag) is " + Boolean.TRUE.equals(flag));
}
}Example explained
Line 1if (flag) needs a primitive, so the compiler inserts flag.booleanValue(), which fails on a null reference.
Line 2flag == null compares references and never unboxes, which is why that line prints normally.
Line 3Boolean.TRUE.equals(flag) answers "is it true" without unboxing, treating null as not true.
&& binds tighter than ||
Shows how grouping, not left-to-right reading, decides the value of a mixed condition.
public class Grouping {
public static void main(String[] args) {
boolean paid = true, member = true, staff = false;
System.out.println(paid || member && staff);
System.out.println((paid || member) && staff);
System.out.println(!paid || member);
System.out.println(!(paid || member));
}
}Example explained
Line 1Line 1 groups as paid || (member && staff); member && staff is false, but paid alone makes the result true.
Line 2The parentheses on line 2 change the grouping, so staff being false now decides the whole expression.
Line 3! applies only to the operand beside it, so !paid || member is (!paid) || member, which is true, while !(paid || member) is false.
Important notes
There is no conversion between int and boolean in either direction, and a cast cannot force one: if (1) and (boolean) 1 are both compile errors.
switch does not accept a boolean, so a two-way choice stays an if/else or a ternary.
Common mistakes
Writing if (done = true) instead of if (done == true): it compiles because the assignment is itself a boolean expression, so done is overwritten and the branch runs on every pass.
Reversing a guard, as in if (s.length() > 0 && s != null): && only shields what stands to its right, so the call happens first and throws NullPointerException.
Typing & where && was meant: false & load() still calls load(), so the guard evaluates exactly the code it was supposed to skip.
Try it yourself
Change, predict, then run
In a browser editor declare String code = null; and write a single if that prints ok only when code is non-null and starts with "AB". Then swap the two operands of the && and rerun to see the NullPointerException that the original order prevented.
Open the Java workspaceCheck your understanding
With int[] data = {}; what happens when the program reaches if (data.length > 0 & data[0] == 1) { }?
- It throws ArrayIndexOutOfBoundsException, because & evaluates both operands.
- It skips the body and continues normally, because the left operand is false.
- It fails to compile, because & is defined only for integer operands.
- It throws NullPointerException, because the array holds no elements.
Show answer
Both operands are boolean, so & is a legal logical and, but unlike && it never skips the right operand: data[0] is read on an empty array and throws ArrayIndexOutOfBoundsException. Option 2 describes what && would do, and that short circuit is precisely the protection missing here.