Bits of Java – Episode 6: Logical Operators and their Short-Circuit Version
Before starting preparing this exam, I had always used the logical operators &&
and ||
, which you all probably are familiar with.
For those who are not, both are binary operators, where the two operands are boolean expressions. The first one returns true
if and only if both the operands are evaluated to true
, while the second one returns true
if at least one of the two operands is evaluated to true
.
int number1 = 3;
int number2 = 7;
int number3 = 12;
/*
* This results to true because both the expressions evaluate to true
*/
boolean result1 = (number1 < number2) && (number2 < number3);
/*
* This results to false because the first expression evaluates to false
*/
boolean result2 = (number1 > number2) && (number2 < number3);
/*
* This results to true because at least one of the expression
* (in this case both of them) evaluates to true
*/
boolean result3 = (number1 < number2) || (number2 < number3);
/*
* This results to false because at least one of the expression (the second one)
* evaluates to true
*/
boolean result4 = (number1 > number2) || (number2 < number3);
The thing that I did not know was that these operators are called short-circuit logical operators, and that they have their own counter part, &
and |
, respectively, which we will call as the basic logical operators. From the logical point of view they work in the same way, namely &
returns true
if and only if both the operands evaluate to true
, and |
returns true
if at least one of the operands evaluates to true
. We should also mention that, while the short-circuit operators can be applied only to boolean expressions, their basic logical operators version can also be applied to numeric expressions.
Why do we need two kinds of logical operators, then? And why the first ones are called short-circuit?
Well, the difference between these two kinds of logical operators is that with the *short-circuit* ones, the second operand expression may not be evaluated.
Let’s explain what I mean with may not be evaluated. Suppose you have two boolean expressions as operands for a &&
evaluation. Well, we have said that the result will be true
if and only if both the operands evaluate to true
. So, if the first one evaluates to false
there is no need to proceed and look at the result of the second operand, because we already know that the overall result will be false
.
A similar reasoning can be applied to the ||
operator. If the first operand evaluates to true
we do not need to go further, because we just need one of the boolean operand to evaluate to true
for the final result to be true
.
And this is exactly what happens:
- if the first operand of an
&&
expression evaluates tofalse
, then the second operand is not evaluated; - if the first operand of a
||
expression evaluates totrue
, then the second operand is not evaluated.
The basic logical operators, &
and |
, instead, always evaluate both the operands of the expression.
One of the main advantages of the short-circuit logical operators with respect to their basic counter parts, and probably the reason why they are usually preferred, is that you can avoid NullPointException
with them.
String word = null;
/*
* This will result in a NullPointException because both the operands of the &
* expression will be evaluated and the second one will throw the Exception
*/
if(word != null & word.length() > 3) {
System.out.println(word);
}
/*
* This will NEVER result in a NullPointException because, if the first operand
* evaluates to false, then the second operand is never evaluated
*/
if(word != null && word.length() > 3) {
System.out.println(word);
}
The fact that the second operand may not be evaluated when using the short-circuit version of the logical operators can also result in what are called unperformed side effects.
int number1 = 3;
int number2 = 7;
int number3 = 12;
boolean result1 = (number1 > number2) && (number2++ == 7);
/*
* Since the first operand of the && expression evaluates to false,
* the second operand is not evaluated, meaning that number2 is
* never incremented and the print statement will print 7
*/
System.out.println(number2);
boolean result1 = (number1 > number2) & (number2++ == 7);
/*
* result1 is evaluated using an & expression, menaing that both the
* operands are evaluated, no matter what the result of the first is.
* So, in this case number2 is incremented and this print statement
* will print 8
*/
System.out.println(number2);
To conclude, keep in mind that there exists two versions of the logical operators, and that the short-circuit one can help you in avoiding NullPointException
at runtime, but, at the same time, can lead to unperformed side effects that you have to take into account!
Stay tuned for the next episode of this series, in which we will discuss the switch
statement!
by Ilenia Salvadori