Java – Decision Making and Branching

 Statements in Java

Till now we have seen two types of executable statements (without counting declarations):
• method invocation
• assignment
These are simple statements by means of which we can write programs
• constituted by sequences of simple statements;
• that do method calls, possibly nested.
Very often, when we have to solve a problem, we are interested in performing different actions depending on
whether certain conditions are true or false.

 Conditional statements

Java, like all other programming languages, is equipped with specific statements that allow us to check a
condition and execute certain parts of code depending on whether the condition is true or false. Such statements
are called conditional, and are a form of composite statement.
In Java, there are two forms of conditional statements:
• the if-else statement, to choose between two alternatives;
• the switch statement, to choose between multiple alternatives.

The if-else statement

The if-else statement allows us to select between two alternatives.
if-else statement
Syntax:
if (condition )
then-statement
else
else-statement
• condition is an expression of type boolean, i.e., a conditional expression that is evaluated to true or
false
• then-statement is a single statement (also called the then-branch of the if-else statement)
• else-statement is a single statement (also called the else-branch of the if-else statement)
12 UNIT 5
Semantics:
First, the condition is evaluated. If the result of the evaluation is the value true, the then-statement is
executed, otherwise the else-statement is executed. In both cases, the execution continues with the statement
immediately following the if-else statement.
Example:
int a, b;

if (a > b)
System.out.println(“bigger value = ” + a);
else
System.out.println(“bigger value = ” + b);
When this if-else statement is executed, the string “bigger value = ” followed by the bigger one among a
and b is printed on the output channel (the monitor).

Condition in an if-else statement

The condition in an if-else statement can be an arbitrary expression of type boolean, for example:
• a variable of type boolean;
Example:
boolean finished;

if (finished)

• one of the comparison operators (==, !=, >, <, >=, or <=) applied to variables (or expressions) of a primitive
type;
Example:
int a, b, c;

if (a > b + c)

• a call to a predicate (i.e., a method that returns a value of type boolean);
Example:
String answer;

if (answer.equalsIgnoreCase(“YES”))

• a complex boolean expression, obtained by applying the boolean operators !, &&, and || to simpler
expressions;
Example:
int a, b, c, d;
String answer;

if ((a > (b+c)) || (a == d) && !answer.equalsIgnoreCase(“YES”))

The if variant

The else part of an if-else statement is optional. If it is missing, we have an if statement, which allows us
to execute a certain part of code if a condition is satisfied (and do nothing otherwise).
if statement
Syntax:
if (condition )
then-statement
• condition is an expression of type boolean
• then-statement is a single statement (also called the then-branch of the if statement)
Semantics:
First, the condition is evaluated. If the result of the evaluation is the value true, the then-statement is
executed, and the execution continues with the statement immediately following the if statement. Otherwise,
the execution continues directly with the statement following the if statement.
Example:
boolean found;

if (!found)
System.out.println(“element not found”);
When this if statement is executed, the string “element not found” is printed on the output channel, provided
the value of the boolean variable found is false.

Block of statements

The syntax of if-else allows us to have only a single statement in the then-branch (or the else-branch). If
we want to execute more than one statement in the then-branch (or the else-branch), we have to use a block
construct. A block of statements groups several statements in a single composite statement.
Block of statements
Syntax:
{
statement

statement
}
• statement is an arbitrary Java statement
Semantics:
The statements in the block are executed in sequence. The variables declared inside the block are not visible
outside the block itself.
Example:
int a, b, bigger;

if (a > b) {
bigger = a;
System.out.println(“smaller = ” + b);
}

 Scope of variables defined in a block

A block of statements can contain variable declarations. The scope of a variable declared inside a block is the
block itself, including other blocks contained in it, if present. This means that the variable is visible in the block
and in all sub-blocks, but is not visible outside the block.
Example:
public class ScopeInBlock {
public static void main(String[] args) {
String a = “Hello”;
int i = 1;
{
System.out.println(a);
// OK. a is visible – prints Hello
//int i;
// ERROR. i is visibile and cannot be redeclared
{
double r = 5.5; // OK
i = i + 1; // OK. i is still visible
System.out.println(r); // OK. r is visible – prints 5.5
}
//System.out.println(r); // ERROR. r is not visible
System.out.println(i); // OK. i is visibile – prints 2
{
int r = 4; // OK. previous r is not visible anymore
System.out.println(a);
// OK. a is still visibile – prints Hello
}
}
i = i + 1; // OK. i is visible
System.out.println(i); // OK. i is visible – prints 3
}
}
 Use of blocks in an if-else statement
The then-branch or the else-branch of an if-else statement can be any Java statement, and in particular it
can be a block.
Example: Given month and year, compute month and year of the next month.
int month, year, nextMonth, nextYear;

if (month == 12) {
nextMonth = 1;
nextYear = year + 1;
} else {
nextMonth = month + 1;
nextYear = year;
}

Nested if ’s

We have a nested if when the then-branch or the else-branch of an if-else statement is again an if-else or
an if statement.
Example: Given day, month, and year, compute day, month, and year of the next day.
int day, month, year, nextDay, nextMonth, nextYear;

if (month == 12) {
if (day == 31) {
nextDay = 1;
nextMonth = 1;
nextYear = year + 1;
} else {
nextDay = Day + 1;
nextMonth = month;
nextYear = year;
}
} else {

}
Nested if ’s with mutually excluding conditions
A common use of nested if’s is when the conditions in the nested if’s are mutually excluding, i.e., no two of
them can be simultaneously true.
Example: Based on the value of the temperature (an integer) print a message according to the following table:
temperature t message
30 < t hot
20 < t ≤ 30 warm
10 < t ≤ 20 fine
t ≤ 10 cold
int temp;

if (30 < temp)
System.out.println(“hot”);
else if (20 < temp)
System.out.println(“warm”);
else if (10 < temp)
System.out.println(“fine”);
else
System.out.println(“cold”);
Observations:
• At the outermost level we have a single if-else statement.
• The order in which the conditions are specified is important.
• The second condition need not be composite, e.g., (20 < temp) && (temp <= 30), since it appears in
the else-branch of the first condition. Hence, we already know that (temp <= 30) is true.
• Each else refers to the if that immediately precedes it.
5.11 Ambiguity of the else in if-else statements
Consider the following code fragment:
if (a > 0) if (b > 0) System.out.println(“b positive”);
else System.out.println(“???”);
System.out.println(“???”) could in principle be the else-branch of:
• the first if: hence we should replace “???” with “a negative”;
• the second if: hence we should replace “???” with “b negative”.
The ambiguity is solved by considering that an else always refers to the nearest if without an associated
else. In the above example we have:
if (a > 0)
if (b > 0)
System.out.println(“b positive”);
else
System.out.println(“b negative”);
It is always possible to use a block (i.e., {..}) to disambiguate nested if-else statements. In particular, if we
want that an else refers to an if that is not the immediately preceding one, we have to enclose the immediately
preceding if in a block. For example:
if (a > 0) {
if (b > 0)
System.out.println(“b positive”);
} else
System.out.println(“a negative”);
Example: type of a triangle
Given three values representing the lengths of the three sides of a triangle, determine whether the triangle is
regular (all three sides are equal), symmetric (two sides are equal), or irregular (no two sides are equal).
A possible algorithm is the following: compare the sides two by two, until we have gathered sufficient information
to decide the type of the triangle.
The algorithm can be implemented as follows:
double first, second, third;

if (first == second) {
if (second == third)
System.out.println(“regular”);
else
System.out.println(“symmetric”);
} else {
if (second == third)
System.out.println(“symmetric”);
else if (first == third)
System.out.println(“symmetric”);
else
System.out.println(“irregular”);
}
Shortcut evaluation of a complex condition
The condition in an if-else statement can be a complex boolean expression, in which the logical operators &&,
||, and ! may appear. We have seen that Java performs a shortcut evaluation of such expressions. In other
words, the subexpressions to which the operators are applied are evaluated from left to right as follows:
• when evaluating (e1 && e2), if the evaluation of e1 returns false, then e2 is not evaluated.
• when evaluating (e1 || e2), if the evaluation of e1 returns true, then e2 is not evaluated.
Consider the case of (e1 && e2). If the value of e1 is false, then the value of the whole expression (e1 && e2)
is false, independently of the value of e2. This justifies why in Java e2 is not even evaluated. Similar
considerations hold for (e1 || e2).
In general, the fact that Java performs a shortcut evaluation of boolean expressions has to be taken into account
and cannot be ignored, since the correctness of the code may depend on that.
Example:
String s;

if (s != null && s.length() > 0) {
System.out.println(s);
}
In this case, when the value of s is null then s.length()>0 is not evaluated and the method length() is not
called. Note that, if Java evaluated s.length()>0 also when s is null, then the above code would be wrong,
since it would cause trying to access via s a nonexistent object.
Note: In general, if-else statements that make use of complex boolean conditions could be rewritten by
making use of nested if-else statements. However, to do so, it may be necessary to duplicate code. We
illustrate this in the following separately for && and ||.
Eliminating the conjunction operator && in a complex condition
The code fragment
if ((x < y) && (y < z))
System.out.println(“y is between x and z”);
else
System.out.println(“y is not between x and z”);
corresponds to
if (x < y)
if (y < z)
System.out.println(“y is between x and z”);
else
System.out.println(“y is not between x and z”);
else
System.out.println(“y is not between x and z”);
In this case, by eliminating the complex condition, the code of the else-branch must be duplicated.
Note that, due to shortcut evaluation, the second condition in the && is not evaluated if the first condition is
false. And this holds also for the corresponding nested if-else statements.
Eliminating the disjunction operator || in a complex condition
The code fragment
if ((x == 1) || (x == 2))
System.out.println(“x equal to 1 or to 2”);
else
System.out.println(“x different from 1 and from 2”);
corresponds to
if (x == 1)
System.out.println(“x equal to 1 or to 2”);
else if (x == 2)
System.out.println(“x equal to 1 or to 2”);
else
System.out.println(“x different from 1 and from 2”);In this case, by eliminating the complex condition, the code of the then-branch must be duplicated.
Again, due to shortcut evaluation, the second condition in the || is not evaluated if the first condition is true.
And this holds also for the corresponding nested if-else statements.

The switch Statement

Unlike if-then and if-then-else statements, the switch statement can have a number of possible execution paths. A switch works with the byte, short, char, and int primitive data types. It also works with enumerated types , the String class, and a few special classes that wrap certain primitive types: Character,Byte,Short, and Integer.

The following code example, SwitchDemo, declares an int named month whose value represents a month. The code displays the name of the month, based on the value of month, using theswitch statement.

public class SwitchDemo {
    public static void main(String[] args) {

        int month = 8;
        String monthString;
        switch (month) {
            case 1:  monthString = "January";
                     break;
            case 2:  monthString = "February";
                     break;
            case 3:  monthString = "March";
                     break;
            case 4:  monthString = "April";
                     break;
            case 5:  monthString = "May";
                     break;
            case 6:  monthString = "June";
                     break;
            case 7:  monthString = "July";
                     break;
            case 8:  monthString = "August";
                     break;
            case 9:  monthString = "September";
                     break;
            case 10: monthString = "October";
                     break;
            case 11: monthString = "November";
                     break;
            case 12: monthString = "December";
                     break;
            default: monthString = "Invalid month";
                     break;
        }
        System.out.println(monthString);
    }
}

In this case, August is printed to standard output.

The body of a switch statement is known as a switch block. A statement in the switch block can be labeled with one or more case or default labels. The switch statement evaluates its expression, then executes all statements that follow the matching case label.

You could also display the name of the month with if-then-else statements:

int month = 8;
if (month == 1) {
    System.out.println("January");
} else if (month == 2) {
    System.out.println("February");
}
...  // and so on

Deciding whether to use if-then-else statements or a switch statement is based on readability and the expression that the statement is testing. An if-then-else statement can test expressions based on ranges of values or conditions, whereas a switch statement tests expressions based only on a single integer, enumerated value, or String object.

Another point of interest is the break statement. Each break statement terminates the enclosing switch statement. Control flow continues with the first statement following the switch block. The break statements are necessary because without them, statements in switch blocks fall through: All statements after the matching case label are executed in sequence, regardless of the expression of subsequent case labels, until a break statement is encountered. The program SwitchDemoFallThrough shows statements in a switch block that fall through. The program displays the month corresponding to the integer month and the months that follow in the year:

public class SwitchDemoFallThrough {

    public static void main(String[] args) {
        java.util.ArrayList<String> futureMonths =
            new java.util.ArrayList<String>();

        int month = 8;

        switch (month) {
            case 1:  futureMonths.add("January");
            case 2:  futureMonths.add("February");
            case 3:  futureMonths.add("March");
            case 4:  futureMonths.add("April");
            case 5:  futureMonths.add("May");
            case 6:  futureMonths.add("June");
            case 7:  futureMonths.add("July");
            case 8:  futureMonths.add("August");
            case 9:  futureMonths.add("September");
            case 10: futureMonths.add("October");
            case 11: futureMonths.add("November");
            case 12: futureMonths.add("December");
                     break;
            default: break;
        }

        if (futureMonths.isEmpty()) {
            System.out.println("Invalid month number");
        } else {
            for (String monthName : futureMonths) {
               System.out.println(monthName);
            }
        }
    }
}

This is the output from the code:

August
September
October
November
December

Technically, the final break is not required because flow falls out of the switch statement. Using a break is recommended so that modifying the code is easier and less error prone. Thedefault section handles all values that are not explicitly handled by one of the case sections.

The following code example, SwitchDemo2, shows how a statement can have multiple case labels. The code example calculates the number of days in a particular month:

class SwitchDemo2 {
    public static void main(String[] args) {

        int month = 2;
        int year = 2000;
        int numDays = 0;

        switch (month) {
            case 1: case 3: case 5:
            case 7: case 8: case 10:
            case 12:
                numDays = 31;
                break;
            case 4: case 6:
            case 9: case 11:
                numDays = 30;
                break;
            case 2:
                if (((year % 4 == 0) && 
                     !(year % 100 == 0))
                     || (year % 400 == 0))
                    numDays = 29;
                else
                    numDays = 28;
                break;
            default:
                System.out.println("Invalid month.");
                break;
        }
        System.out.println("Number of Days = "
                           + numDays);
    }
}

This is the output from the code:

Number of Days = 29

Using Strings in switch Statements

In Java SE 7 and later, you can use a String object in the switch statement’s expression. The following code example, StringSwitchDemo, displays the number of the month based on the value of the String named month:

public class StringSwitchDemo {

    public static int getMonthNumber(String month) {

        int monthNumber = 0;

        if (month == null) {
            return monthNumber;
        }

        switch (month.toLowerCase()) {
            case "january":
                monthNumber = 1;
                break;
            case "february":
                monthNumber = 2;
                break;
            case "march":
                monthNumber = 3;
                break;
            case "april":
                monthNumber = 4;
                break;
            case "may":
                monthNumber = 5;
                break;
            case "june":
                monthNumber = 6;
                break;
            case "july":
                monthNumber = 7;
                break;
            case "august":
                monthNumber = 8;
                break;
            case "september":
                monthNumber = 9;
                break;
            case "october":
                monthNumber = 10;
                break;
            case "november":
                monthNumber = 11;
                break;
            case "december":
                monthNumber = 12;
                break;
            default: 
                monthNumber = 0;
                break;
        }

        return monthNumber;
    }

    public static void main(String[] args) {

        String month = "August";

        int returnedMonthNumber =
            StringSwitchDemo.getMonthNumber(month);

        if (returnedMonthNumber == 0) {
            System.out.println("Invalid month");
        } else {
            System.out.println(returnedMonthNumber);
        }
    }
}

The output from this code is 8.

The String in the switch expression is compared with the expressions associated with each case label as if the String.equals method were being used. In order for theStringSwitchDemo example to accept any month regardless of case, month is converted to lowercase (with the toLowerCase method), and all the strings associated with the case labels are in lowercase.

 

Leave a comment