8. Flow control#

This is a straightforward listing of the Java flow control structures.

8.1. Deciding with if/else if/else#

If/else is the basic decision-making mechanism in every programming language. In Java, both “else if” and “else” are optional.

if (condition) {} 
[else if (condition) {}]*
[else {}]?

So, the “if” block is the required basis, “else if” can be used zero to many times - but only directly following the “if”, and the entire statement is closed by an optional “else”.

So these are not legal:

if (foo) { }
else { }
else if (bar) { }

(the else if() comes after the else)

if (foo) { }
else { }
else { }

(two else blocks)

if (foo) { }
int number = 2;
else { }

(the int number = 2; interrupts the if/else block)

But this is legal:

if (foo) { }
else if { 
    if (bar) { }
    else { }    
}
else { }

8.1.1. Single-line blocks#

When there is a single statement in a block (the part between the curly braces) you are allowed to keep it on the same line and omit the braces, as in this example:

boolean foo = getFoo();
if(foo) System.out.println("Foo is true!");
else System.out.println("The foo says no...");

But in such cases you may want to consider using the ternary operator:

boolean foo = getFoo();
String message = (foo ? "Foo is true!" : "The foo says no...")
System.out.println(message);

8.2. Looping with for#

The “classic” control structure with for iterates over a collection or a defined series of steps:

for (init; condition; change) { } 
  • “init” is the loop initialization; declaring a counter usually.

  • “condition” is the test that determines whether the loop should run once more

  • “change” is the iteration change; usually an increment of the init.

This is a typical for-loop:

String[] nucleotides = {"Adenine", "Cytosine", "Guanine", "Thymine"};
for( int i = 0; i < nucleotides.length; i++) {
    System.out.println("nucleotide " + i + " is " + nucleotides[i]);
}

All three elements are optional. This, for instance, is also a legal for-loop:

String[] nucleotides = {"Adenine", "Cytosine", "Guanine", "Thymine"};
int i = nucleotides.length - 1;
for( ; i >= 0; --i) {
    System.out.println("nucleotide " + i + " is " + nucleotides[i]);
}

Note that the change does not need to be an increment but can be any change, like the decrement in this example.
Even this one is legal:

for(;;) {
    System.out.println("Hello ");
}

//same as 
while(true) {
    System.out.println("Hello");
}

But it will run into eternity (or until ctrl + c has been typed). Actually, variations of the second form are quite often used to wait for user input in a terminal setting:

 while(true) {
    int answer = getUserInput();
    if (answer == 42) break;
}

8.2.1. foreach#

The for-loop also has a variant. It is called the foreach loop. The difference is that there is no increment or condition; it is simply used to iterate a collection (Arrays or Collection types dealt with later).

for (element : collection) { } 

And this is a working example:

String[] nucleotides = {"Guanine", "Adenine", "Cytosine", "Thymine"};
for( String nucleotide : nucleotides) {
    System.out.println("nucleotide = " + nucleotide);
}

8.3. Looping with while#

The while loop is used to loop when there is no predefined number of iterations and/or no underlying collection.

It has two variants:

while (condition) { } 

and

do { } while (condition) 

The difference is that do{} while() is guaranteed to execute at least once, whereas while(condition) {} can be skipped entirely if “condition” is false at the first evaluation.

8.4. break and continue#

All iteration structures (the while and for variants) and the switch structure have the possibility to leave the current iteration early, or the loop entirely.

  • break - leave the loop (or the switch block)

  • continue - abort current iteration of the loop and go to next iteration

Here the loop is designed to do nothing with Scorpions.

List<String> zoo = List.of("Giraffe", "Mouse", "Scorpion", "Zebra");
for (String animal : zoo) {
    if (animal.equals("Scorpion")) continue;
    System.out.println("animal = " + animal);
}

8.5. Discrete options with switch/case#

The switch/case structure is a decision flow control structure that is always replaceable by if/else. However, switch/case is often more efficient, and better suited to deal with choosing between different discrete options. Where if/else works with boolean conditions, switch/case works with discrete cases.

Caution

This structure requires compile time constants to be legal (compilable). These include booleans, Strings, integers and enums, but not float or double or mutable object instances.

The formal description is

switch (actual case) {
    [case  case n: {case specific statement(s)}]*
    [default: {default statement(s)}]?
}

So there are one to many possible case-specific actions and zero or one default actions.

Here is an example method using a switch/case:

void switchCase(String country) {
    switch (country) {
        case "Netherlands":
            System.out.println("Some weather, huh?");
            break;
        case "Belgium":
            System.out.println("Let's get a beer!");
            break;
        default:
            System.out.println("What shall we talk about?");
    }
}

and when this is run like this

FlowControlDemo demo = new FlowControlDemo();
demo.switchCase("Netherlands");

we get this output

Some weather, huh?

and when called with an unknown country the default will be printed.

Note the break keyword in each case. The break is a very important aspect of the switch block. A switch block is fall-through unless a break is encountered.

So if the breaks are removed from the switch

void switchCase(String country) {
    switch (country) {
        case "Netherlands":
            System.out.println("Some weather, huh?");
            //break;
        case "Belgium":
            System.out.println("It is Belgian fries!");
            //break;
        default:
            System.out.println("Have a beer?");
    }
}

we get this output

Some weather, huh?
It is Belgian fries!
Have a beer?

Although this seems rather illogical, there are uses for this behavior. For example, consider this error handler for a web application:

public String errorMessage(int errorCode) {
    String message;
    switch (errorCode) {
        case 401:
        case 402:
        case 403:
        case 405:
            message = "You are trying to do something that is not allowed";
            break;
        case 418:
            message = "I am a teapot; I cannot brew coffee";
            break;
        case 404:
        case 503:
            message = "Resource not found";
            break;
        default:
            message = "Some exotic error occurred. Try again later";
    }
    return message;
}

When called like this

void errorMessageTest() {
    FlowControlDemo demo = new FlowControlDemo();
    String errorMessage = demo.errorMessage(402);
    System.out.println(errorMessage);
    errorMessage = demo.errorMessage(502);
    System.out.println(errorMessage);
}

We get these messages:

You are trying to do something that is not allowed
Some exotic error occurred. Try again later