|
The short circuit operators && and || provide the logical operations for boolean operands. Like bitwise &, the && (short-circuit AND operator) returns true when both operands are true. Similarly, the || (short circuit OR operator) returns true if any one (or both) of its operands is true. So why do we need them when Java already has the & and | operators doing the same thing. The reason is that the short circuit operators can sometimes optimize ("short-circuit") the evaluation of logical operations so that there is no need to calculate the second operand.
How && and || optimize (short-circuit)?
The short circuit operators && and || make use of two basic digital logic rules for optimization:
In AND operation, if the first operand is false, the output is false irrespective of the second operand.
In OR operation, if first operand is true, the output is true irrespective of the second operand.
If you take a quick look at table 9.6, you can countercheck these two rules. The && and || operators use these rules to optimize the AND and OR operations respectively. This optimization is more effective when you have complex expressions as operands. Following example uses the && operator on two boolean expressions.
int a = 0, b = 8;
boolean result = (a != 0) && (b == 8);
The first expression (a != 0) evaluates as false. Since AND operation returns true only when both the operands are true, there is no point in evaluating the second expression (b == 8) because it will not change the value of result. Therefore, the && operators takes a short cut and abandons the execution of second expression.
If the first expression evaluates to true, then no short cut is adopted and the second expression is also evaluated. In the following example, both expressions are evaluated:
int a = 0, b=8;
boolean result = (a == 0) && (b == 8);
Now since first expression (a == 0) evaluates to true, the && checks whether the second expression is true as well. Since (b == 8) is also true, the result will be true.
|
|
You need to keep in mind that the second operand of && is sometimes evaluated and sometimes not. The execution of second operand very much depends on the value of the first operand. This is also true about the short-circuit OR operation. So do not wrongfully assume that the evaluation of second expression is always skipped. |
The || operator short-circuits the OR operation in the similar way. If the first operand evaluates to true, the result is going to be true irrespective of the second operand. In such case, the second operand is not evaluated. In the following example, the || operator performs OR on two boolean expressions:
int a = 0, b=8;
boolean result = (a == 0) || (b == 8);
Since the first expression (a == 0) evaluates as true, the result will be true irrespective of second operand. Therefore the execution of second expression (b == 8) is abandoned.
Note that these short cuts do not affect the final output. If you apply & instead of && or | instead of ||, the final output will be same. However, sometimes the short circuit operators produce some side effects. For instance, assume that the second operand of && contains expression that modifies some variable in some way. In the following example, the second expression increments value of variable b by 1.
int a = 0, b = 8;
boolean result = (a == 0) || (b++ == 8);
Now since the first operand (a == 0) of || evaluates true, the second expression will not be evaluated. Therefore, the variable b will not be incremented. However, if you use the | instead of ||, the second expression will definitely execute. In that case, the variable b will be incremented. Thus the result will be same whether you use || or |. However, the value of b will be different. Table 9.7 shows the how the short circuit operator || produce same output as the | operator, but can cause different side effects on value of variable b.
Table 9.7 Difference between execution of || and | operators
|
|
Using Short circuit operator |
Using logical operator |
|
Source code |
int a = 0, b = 8; boolean result = (a == 0) || (b++ == 8); System.out.println("result = " + result); System.out.println("a = " + a); System.out.println("b = " + b);
|
int a = 0, b = 8; boolean result = (a == 0) | (b++ == 8); System.out.println("result = " + result); System.out.println("a = " + a); System.out.println("b = " + b);
|
|
Output |
result = true a = 0 b = 8 |
result = true a = 0 b = 9 |
The b is not incremented when || is used as it skips the evaluation of second operand. The && can cause similar side effects as it too skips evaluation of second operand when its first operand evaluates false.
Use of short circuit && to avoid exceptions
If string reference is null, calling any method on it would cause NullPointerException at run time.
private String processName(String name) {
if(name != null) {
if(name.length() != false) {
// process the name
}
}
return name;
}
If you decide to combine the two conditions to make code concise, you can do so with operator & as :
if(name != null & name.length() != false) {
// process the name
}
However, whenever name is null, the above code will cause a NullPointerException. This because even when name is null, the second expression is evaluated and name.length() will throw NullPointerException. You can avoid it if you use the short circuit && operator. It will check whether name is null before calling the length() method on it as:
private String processName(String name) {
if (name != null && name.length() != false) {
// process the name
}
return name;
}
Now, whenever the name is null, the second condition is not evaluated. Thus, we have successfully avoided the NullPointerException and used the short-circuit behavior to our advantage.
With the logical operators && || & and |, the discussion of binary operators is coming to an end. There is more to binary operations such as operands compatibility, the arithmetic promotion etc which we will be discussing in next chapter. The next section discusses the one and only Java operator that operates on three operands.