Monday, October 9, 2017

Control Statements - If and Switch

Control statmements are used to determine the flow of execution in a program. In the previous sections, the code was executed line by line, in the order it was written. By using control statements, we are able to create programs that take different paths depending on the state of the application. Let's look at an example:

System.out.print("Enter your age: ");
Scanner scanner = new Scanner(System.in);
int age = scanner.nextInt();
if (age < 13) {
System.out.println("You are young");
}
else if (age < 20) {
System.out.println("You are a teenager");
}
else if (age <= 50) {
System.out.println("You are middle aged");
}
else {
System.out.println("You are old");
}
scanner.close();

In the example above, only one of the four statements will be printed to the screen ("You are young", "You are a teenager", "You are middle aged", "You are old"). The program is not run line by line, and three of println statements won't be run at all. By using control statements, we're able to create dynamic programs that do different things in different situations. There are three types of control statements: selection statements, iteration statements, and jump statements. Let's start with selection statements, and we'll cover iteration and jump statements in the next sections.

Selection statements are used to determine which statement(s) are run at a certain time. The two types of selection statements in Java are if statements and switch statmenets. You saw an example of if statements in the last example. Here is the general form of if statements:

if (boolean condition)
{
//some action
}
else
{
//some other action
}

If the boolean condition evaluates to true, the code inside of the if statement is executed, otherwise the code inside of the else statement is executed. Another common way of using selection statements is by using a series of else if statements:

if (condition1) {
//action1
}
else if (condition2) {
//action2
}
else if (condition3) {
//action3
}
else {
//action4
}

In this case, only 1 of the 4 actions will be executed. The if statements are evaluated from top to bottom. If condition1 is true, then action1 is taken and the other actions are skipped. Otherwise, condition2 is evaluated, and action2 be run if condition2 evaluates to true, and the other actions are skipped. The same logic applies to condition3. Finally, if condition1, condition2, and condition3 all evaluate to false, then action4 is taken. One thing to watch out for is that only the first condition should be an if statement, the rest should be else if or else statements. Take a look at this example:

int age = 10;
if (age < 13) {
System.out.println("You are young");
}
if (age < 20) {
System.out.println("You are a teenager");
}
if (age <= 50) {
System.out.println("You are middle aged");
}
else {
System.out.println("You are old");
}

Here is the output of the program:

You are young
You are a teenager
You are middle aged

Since 10 is less than 13, 20, and 50, all of the if statements evaluate to true and are executed. There may be a few cases where you want this behavior, but most of the time this is an indicator of a bug in the code.

An alternative to using a bunch of if and else if statements is using a switch statement. The switch statement is used to evaluate a bunch of cases in order, and execute the first case that evaluates to true. Let's look at an example using if statements and see how it can be converted to a switch statement:

System.out.println("Java game menu");
System.out.println("1: new game");
System.out.println("2: continue");
System.out.println("3: high scores");
System.out.println("4: options");
System.out.println("5: quit);
System.out.print("Enter menu option: ");
Scanner scanner = new Scanner(System.in);
int option = scanner.nextInt();
if (option == 1) {
System.out.println("Starting new game");
}
else if (option == 2) {
System.out.println("Continuing game");
}
else if (option == 3) {
System.out.println("High score list...");
}
else if (option == 4) {
System.out.println("Options menu...");
}
else if (option == 5) {
System.exit(0); //this terminates the program
}
else {
System.out.println("Invalid option, please try again");
//retry logic
}

This is an example of a simple text-based menu for a java game. Although it works fine, having lots of else if statements in the code can make it a bit harder to follow. Using a switch statement can simplify the menu:

System.out.println("Java game menu");
System.out.println("1: new game");
System.out.println("2: continue");
System.out.println("3: high scores");
System.out.println("4: options");
System.out.println("5: quit);
System.out.print("Enter menu option: ");
Scanner scanner = new Scanner(System.in);
int option = scanner.nextInt();
switch (option) {
case 1:
System.out.println("Starting new game");
break;
case 2:
System.out.println("Continuing game");
break;
case 3:
System.out.println("High score list...");
break;
case 4:
System.out.println("Options menu...");
break;
case 5:
System.exit(0);
//no need to break here, program has already exited
default:
System.out.println("Invalid option, please try again");
//retry logic
}

This code is equivalent to the if statements used above, but is easier to follow. One thing to watch out for is remembering to put break; at the end of each case. This is a keyword that causes all of the other cases to be skipped. Otherwise, the default behavior is to automatically run the rest of the cases below. For example, if the break; statements were removed, you'd see this output for menu option 2:

Continuing Game
High score list...
Options menu...

You wouldn't run into this issue when using if statements. If the menu option is 2, it cannot be 3, 4, or 5, so those println statements wouldn't be executed. Nevertheless, switch statements are a good option when there's a lot of conditions to be evaluated.

Switch statements support byte, short, char, and int data types. It also supports enums and Strings, which we haven't used a lot yet but will be covered in later sections. One big limitation of switch statements is that they can only test for equality, whereas if statements can use any boolean condition. For example, in the age example earlier, we printed out "you are young" if the age is less than 13. If we used a switch, we'd have to write out all values from 0 to 12. For this reason, if statements tend to be a lot more flexible than switch statements, and are used a majority of the time in practice.

One other thing to know about if statements and switch statements is that they can be nested inside of each other. Here is an example with if statements:

int seinfeldSeasonNumber = 1;
int seinfeldEpisodeNumber = 5;

if (seinfeldSeasonNumber == 1) {
System.out.println("First season of Seinfeld");
if (seinfeldEpisodeNumber == 1) {
System.out.println("First ever episode of Seinfeld");
else if (seinfeldEpisodeNumber == 5) {
System.out.println("Last episode of the first season of Seinfeld");
}
} else if (seinfeldSeasonNumber <= 8) {
System.out.println("One of the middle seasons");
} else if (seinfeldSeasonNumber == 9) {
System.out.println("Last season of Seinfeld");
} else {
System.out.println("Invalid season");
}

Running this program produces the following output:
First season of Seinfeld
Last episode of the first season of Seinfeld

In this example, we first check the season number of Seinfeld. If it's 1, then we know it's the first season. We then check the episode number to see if it's the first or last episode of the first season. We could add additional checks for middle episodes in the first season if needed. But the takeaway here is that nesting if statements is useful when we need to divide things into categories and subcategories. You can also nest switch statements in the same way as the if statement example. But remember that switch statements are more limited because they can only check for equality, and I've haven't come across a real-world example where nested switch statements was needed.

Let's end this section by looking at the circle area example we saw at the end of the last section:

import java.util.Scanner;

public class ExpressionsTest {
public static void main(String[] args) {
System.out.println("This program computes the area of a circle given the radius.");
System.out.print("Enter the radius of the circle in centimeters: ");
Scanner scanner = new Scanner(System.in);
double circleRadius = scanner.nextDouble();
double circleArea = circleRadius * 2 * Math.PI;
System.out.println("The area of the circle is " + circleArea + " centimeters");
scanner.close();
}
}

I'd like to add an enhancement to this program to print "Small circle" if the area of the circle is less than 10 square centimeters, "Medium circle" if the area is greater than or equal to 10 square centimeters but less than 50, and "Large circle" for anything greater than or equal to 50 square centimeters. Also, print out "Invalid area" if the user entered a negative value for the circle radius. Go ahead and give this a try on your own. I think that by writing code on your own for small projects is one of the best ways to learn a new programming language. Once you feel that you have something that works, you can check out my solution below.

So, were you successful? Here's my approach to solving this problem:

import java.util.Scanner;

public class ConditionalsTest {
public static void main(String[] args) {
System.out.println("This program computes the area of a circle given the radius.");
System.out.print("Enter the radius of the circle in centimeters: ");
Scanner scanner = new Scanner(System.in);
double circleRadius = scanner.nextDouble();
double circleArea = circleRadius * 2 * Math.PI;
System.out.println("The area of the circle is " + circleArea + " centimeters");
if (circleArea < 0) {
System.out.println("Invalid area");
} else if (circleArea < 10) {
System.out.println("Small circle");
} else if (circleArea < 50) {
System.out.println("Medium circle");
} else {
System.out.println("Large circle");
}
scanner.close();
}
}

There are a few things worth noting in this example. First, I had to use an if statement instead of switch, because the circleArea is a double, and switch does not support double. The reason for this is simple, double is a decimal value so it could be almost anything. And since switch only matches on equality, it would be pointless to use that. For example, checking if a value is 1.4 wouldn't work if I also wanted to accept 1.41 or 1.40000001. Second, I first checked if the circleArea was less than 0 and printed out "Invalid area" in that case. You could also check if circleRadius is less than 0, since they will always have the same sign. Third, I used a series of else if statements after the first if to make sure that only one of the four lines was printed ("Invalid area", "Small circle", "Medium circle", "Large circle"). I also changed the name of the class from ExpressionsTest to ConditionalsTest to reflect that we're now focusing on conditional statements. It's always a good practice to rename classes or variables as the meaning of the code changes over time.

That covers everything we need to know about conditionals for now. We'll move onto iteration statements, which give a very powerful way to repeatedly take actions until a certain condition is met.