15-100 Lecture 11 (February 11, 2008)

A Quick Reminder: The boolean Type

We've talked a bit about the boolean type. And, we've made use of it both in understanding the predicate of if-statements and also to model data, for example, the presence or absence of an AV system within a classroom.

In the example below, a variable of the boolean type, is used to model the state of a light switch:

boolean lightsOn = true;
lightsOn = false;

In the example below, a boolean variable is assigned the true or false result of a boolean expression, as follows:

boolean aliceIsTaller = (aliceHeight > bobHeight);

The ||-logical-or and the &&-logical-and Operators

Boolean expressions can be joined together using the logical AND and logical OR operators. && is the logical and. If boolean expressions are joined together with the &&-operator, both must be true for the whole expression to be true. Similarly, if they are joined together with the ||-operator, only one must be true. Please consider the following example:

boolean needAirConditioning = ( (temp > 75) || (relativeHumidity > 0.65));
boolean okayToSnack = ( (hoursSinceLastMeal > 3) && (hoursTillNextMeal > 2 ) );

Short-Circuit Evaluation

In evaluating expressions joined by the &&-and or the ||-or operators, Java will move from left-to-right. It is important to realize that it might not be necessary for Java to work its way all the way to the end in order to determine the value of the expression.

Consider two terms combined via the &&-and operator. if the left operator is false, there is no reason to consider the right operator -- the expression is necessarily false. As a result, Java will not consider the right operand if the left operand is false -- the evaluation will be "short circuited".

Similarly, if we consider two terms combined via the ||-or operator, the expression is true if either one, or btoh, of the operands are true. As a result, it is only necessary to look at the right operand if the left operand is false. If, however, the left operand is false, the right operand need not be considered. As a result, Java will not consider it. As before, Java short-circuits the evaluation.

Although short-circuit evaluation does not have an effect on the value of the expression, that doesn't imply that it is equivalent to an exhaustive evaluation. Remember, the boolean operands can be literal values, operands -- or boolean functions. In the event that we short-circuit before evaluating a boolean function, it will never be called. ths is normally not a problem, becuase most function do not have side-effects.

But, if the function does have one or more side-effects, they will not occur if short-circuiting prevents it from being called. What is a side-effect? Any persistent change of state resulting from a function call.

For example, if one of these functions would have changed some property of an object, that property would remin unchanged if the method were not called as a result of short-circuiting.

The ! operator

We've made use of the !-negation operator, but it hasn't previously made it into the notes. So, here it is. The !-operator, sometimes known as the negation or NOT operator, turns true into false and vice-versa.

boolean aliceIsTaller = (aliceHeight > bobHeight); if (!aliceIsTaller) { System.out.println ("Bob is at least as tall as Alice."); }

The Switch Statement

Java's switch statement is designed to support decision making where there are more than two possibilities. You provide a laundry-list of possibilities. Which of them, if any, are selected depend on a single predicate.

Within the switch statmeent, there is a laundry list of "cases". Execution procedes down this laundry list. Nothing happens until one of the cases actually matches the value of the predicate. At that point, a "big switch in the sky" is thrown and everything thereafter is executed. And, everything means everything -- even if it is associated with a case that doesn't match. This is because the switch is already thrown.

To avoid this behavior, so that exactly one path can be selected, the code associated with each case is often followed by a "break statment". It works as it did within a loop. A "break" casues control to immediately leave the switch and pick up after it.

Additionally, the "default" case can be specified. This case matches anything. It alwasy cases the great switch in the sky to be turned on.

This is best illustrated with an example. Let's consider our program of last class, rewritten using a switch.

Detangled with Switch

Rememebr this program? Look how pretty it is:

boolean quit = false;

while (!quit) {

System.out.println ("");
System.out.println ("");
System.out.println ("F)irst name");
System.out.println ("M)iddle name");
System.out.println ("L)ast name");
System.out.println ("Q)uit");
System.out.println ("");

// Get choice
Scanner keyboard = new Scanner(System.in);
char choice = keyboard.next().charAt(0);

// Decide what to do -- and do it
switch (choice) {

case 'F':
System.out.println ("Gregory");
break;

case 'M':
System.out.println ("Michael");
break;

case 'L':
System.out.println ("Kesden");
break;

case 'Q':
// Notice: Can't just break here -- we'd leave the switch, not the loop
quit = true;
break;

default:
// Since this is the last case, not strictly necessary, but good form
System.out.println ("Please select F, M, L, or Q.");
break;

}

}

...wasn't that nice?

A Closer Look at the Break (or Absence, Thereof)

Now, let's take a closer look at the break. Let's see how we can avoid the need to upperCase() the string, by accepting either lower- or upper-case, directly.

boolean quit = false;

while (!quit) {

System.out.println ("");
System.out.println ("");
System.out.println ("F)irst name");
System.out.println ("M)iddle name");
System.out.println ("L)ast name");
System.out.println ("Q)uit");
System.out.println ("");

// Get choice
Scanner keyboard = new Scanner(System.in);
char choice = keyboard.next().charAt(0);

// Decide what to do -- and do it
switch (choice) {

case 'f':
case 'F':
System.out.println ("Gregory");
break;

case 'm':
case 'M':
System.out.println ("Michael");
break;

case 'l':
case 'L':
System.out.println ("Kesden");
break;

case 'q':

case 'Q':
// Notice: Can't just break here -- we'd leave the switch, not the loop
quit = true;
break;

default:
// Since this is the last case, not strictly necessary, but good form
System.out.println ("Please select F, M, L, or Q.");
break;

}

}

Take a look at the back-to-back use of upper- and lower-cases. This works because there is no break after the first case -- only the second. So, if the lower case "turns the switch on", it stays on until it hits the break.

Switch Statment, Important Notes

A few things to keep in mind:

• The "switch" must be based on an "ordinal" type: int, long, char, boolean. It can't be based on somethng that requires complicated matching, such as float, double, String (or any Object), &c.

• The "default" case is not requied. It is completely optional.

• You should always end a case with a break -- even if it is the last one. During maintenance, someone might just add a new case underneath without noticing the absence of the break.