Check the buyer's ID. If the customer is 21 or over, sell them beer, otherwise, send them home.After inspecting the shopper's ID, the owner is making a choice. We can show the choice and the two possible outcomes with a decision tree.
We can express this in Java as follows:
IDCard id = aPerson.getID(); if (id.getAge() >= 21) { store.sellBeerTo(aPerson); } else { store.remove(aPerson); }The if-statement in Java has the following pattern:
if (<condition>) { <then clause> } else { <else clause> }The semantics is as follows: The
condition
is first
evaluated, and it must evaluate to a boolean value. If the expression
evaluates to true, the statements of the then-clause
are
executed. Otherwise, the statements of the else-clause
are executed.
Let's refine our algorithm, so it's more like the actual one used:
If the customer looks over 31, sell beer. Otherwise, check their ID. If they are 21 or over, sell them beer, otherwise send them home.Let's draw the decision tree.
We might write this like this in Java:
if (aPerson.apparentAge() > 31) { store.sellBeerTo(aPerson); } if (aPerson.getID().getAge() >= 21) { store.sellBeerTo(aPerson); } else { store.remove(aPerson); }This almost works. What's wrong with it? Think about a 50 year-old. The mistake becomes evident when we look at the decision tree.
The bug in the above example is that we have written our if statement as a collection of two separate if statements. In some cases, (when the person appears to be older than 31 and is actually at least 21) they will be sold beer twice! What we really want to say is:
if (aPerson.apparentAge() > 31) { store.sellBeerTo(aPerson); } else { if (aPerson.getID().getAge() >= 21) { store.sellBeerTo(aPerson); } else { store.remove(aPerson); } }When writing if-statments, we need to make decisions about exclusivity and order. In the above example, we mean for exactly one outcome to occur and we enforce this fact by the way we've constructed our program.
Java provides a set of operators, which are useful for comparing numbers. These include: <, >, >=, <=, !=, ==. Each of these operators compares two subexpressions, evaluating to a boolean (true or false) result. != and == are special because they can also be used to compare subexpressions that are not of numeric types. We'll see examples of this later. The following table summarizes the meaning of these operators:
Symbol | Meaning | Example Expression | Value if y is 11 |
---|---|---|---|
> | greater than | y > 5 | true |
< | less than | y < 5 | false |
>= | greater than or equal | y >= 11 | true |
<= | less than or equal | y <= 10 | false |
!= | not equal | y != 5 | true |
== | equal | y == 5 | false |
Java also provides a set of operators which are useful for combining or transforming boolean expressions. Suppose you want to know if someone's age is between 21 and 31. You can actually already write this:
if (age >= 21) { if (age <= 31) { // do something } else { } } else { }This can be expressed more directly, however, with a boolean operator:
if (age >= 21 && age <= 31) { // do something } else { }Note that we aren't doing anything inside of our else clause. In this case, it is allowable to simply drop the empty else clause, like so:
if (age >= 21 && age <= 31) { // do something }The following table summarizes the boolean operators:
Symbol | Meaning | Example Expression | Value if y is 11 |
---|---|---|---|
&& | and (true when both subexpressions are true) | (y > 5) && (y < 11) | false |
|| | or (true when either or both subexpressions are true) | (y < 5) || (y == 11) | true |
! | not (true when subexpression is false) | !((y > 5) && (y < 11)) | true |
Again, there are precedence rules for boolean operators (generally they have lower precedence than arithmetic or comparison operators) but you can always use parentheses to be sure and to enhance readability of your code.
Let's revisit our refined beer-selling algorithm, and use boolean operators to express it more directly. When we think about it, we see that there are really just two outcomes, selling beer and removing the customer. We sell beer when the customer looks over 31 or when their ID shows them to be 21 or over. We remove the customer in all other cases.
if (aPerson.apparentAge() > 31 || aPerson.getID().getAge() >= 21) { store.sellBeerTo(aPerson); } else { store.remove(aPerson); }
class BankAccount { int number; double balance; String name; void withdraw(double amount) { this.balance = this.balance - amount; } }Conditionals allow us to modify the body of the withdraw method to handle this situation. What is the right action to take? At the very least, we should guard against overdrawing the account. Beyond that, there are several options. We would like the client of our class to be informed that an unexpected situation has occurred. We'll see the correct approach to notifying the client in a later lesson, but for now we'll use a strategy that relies on returning a "code" that tells the client whether their operation was successful. We'll return a boolean value: true meaning that the operation succeeded; false meaning it failed.
public class BankAccount { private int number; private double balance; private String name; // All the other methods... public boolean withdraw(double amount) { if (amount <= this.balance) { this.balance = this.balance - amount; return true; } else { return false; } } }The withdraw method now checks for sufficient funds in the account before deducting the withdrawal amount from the current balance. If there are insufficient funds, no change is made to the balance, and the value false is returned. This approach puts the onus on the client of the class to check to return value to see if the operation succeeded. A typical fragment of client code might look like this:
BankAccount account = new BankAccount(1234, 225.34, "Bill"); Input input = new Input(); double amount = input.readDouble("How much money do you want?"); boolean succeeded = account.withdraw(amount); if (succeeded) { System.out.println("Here's your money!!"); } else { System.out.println("Sorry, insufficient funds!"); }