Friday, February 4, 2011

Taking shortcuts

Now that I finished going through all of the Java operators, I want to talk about the special way that two of them work. Those two are the logical AND and OR operators, also known as && and ||. These guys can be lazy and skip part of their evaluation; sometimes this is good for you, and sometimes not.

Let's take logical AND. If both its operands in an expression are true, then the whole thing evaluates to true. Otherwise, the expression evaluates to false. That's just AND.

Now, let's focus on that first operand. If it evaluates to true, what does that tell you about the expression's value? Well, not much, you still need to check on the second operand. But suppose the first operand evaluates to false? If that happens, you know that the whole expression is false already. The value of the second operand doesn't matter.

Java takes this lesson to heart. If the first operand for logical AND evaluates to false, then it doesn't evaluate the second operand at all. This is called a shortcut or short circuit Boolean operation. It is good for efficiency, especially for something like this:
if (debugging && timeConsumingMethod()) {
  doSomething();
}
Because logical AND shortcuts, that really expensive timeConsumingMethod won't be called unless you are debugging. Might as well not pay the price if you can help it.

Logical OR works the same way, except opposite sorta. If the first operand in its expression is true, then it shortcuts, because the entire expression must evaluate to true at that point no matter what. For example:
if (answer == null || !(answer.equals ("apple"))) {
  wrongAnswer();
}
Without short circuiting, the equals() call would throw a nasty NullPointerException when answer is null. Instead, though, because OR shortcuts, it's safe. This is a common trick for dealing with nulls in comparisons.

While shortcut operations are useful, you do have to be careful with them. Avoid having the second operand do something as a side effect that you really want to happen all the time.
int i = 0;
while (i < a.length) {
  if (a[i] == null || a[i++].equals ("")) {
    System.out.println ("I found a blank!");
  }
}
This all-too-clever loop tries to run through an array of strings, looking for either nulls or empty strings. The loop index gets incremented with that i++ bit. As long as the array contains no nulls, this loop will work, but as soon as the first null arrives, because logical OR shortcuts, the loop will get stuck, printing out "I found a blank!" forever and ever, or until you hit Control-C.

(I don't think I've covered the while loop yet. It's a basic loop that keeps going "while" the Boolean expression next to while evaluates to true. If the expression is false to begin with, the loop never executes even once. So, you have to make sure you either make that condition false at some point, or break out of the loop using either break or return.)

The example above is a bit contrived, but traps like it can and do happen more subtly in real code.

Two more things. First, the bitwise AND and OR operators, known as & and |, do not short circuit. They are more akin to mathematical operations, so this makes sense.

Second, don't use the shortcut feature to implement flow control. This is something that is idiomatic in scripting languages, but it ain't the Java way. Here's some examples. First, the typical way you open a file in Perl.
open HANDLE, "<myfile.txt" or die "Cannot open myfile.txt";
The die command, which causes the script to exit, will not execute as long as the file is opened successfully.

Another example, from bash scripting:
[[ -n DEBUG ]] && echo File is opened.
If the DEBUG variable isn't set, the AND short circuits and the debug message isn't printed out.

While this is pretty nifty and all, doing the same thing in Java will likely lead to confusion, although it may help your geek cred. Eh, it's not worth it. Really, use if statements instead.

No comments:

Post a Comment