Many-Way Decisions
What does a program do when a question has more than two answers?
By the end of this lecture you will be able to
- Write an
else-ifchain and put its conditions in the right order - Use nested conditions and tell which
ifeachelsebelongs to - Write
switchwithint,charandString, and controlbreakanddefault - Choose between
if,switchand the ternary operator to suit the problem - Build complete programs: a menu calculator, days in a month, leap years and a cash machine
In this lecture
- Many questions have more than two answers
- A chain runs the first branch whose condition is true, then skips the rest
- The order of conditions changes the result: start with the narrowest
- The word else is what ties conditions into one chain
- A final else guarantees the variable gets a value
- A nested if asks its question only after an earlier one succeeds
- else matches the nearest open if before it, whatever the indentation says
- Leap years: one rule that combines three conditions
- switch jumps to the case equal to the value
- Without break the program carries on into the next cases
- Deliberate fall-through groups several cases into one answer
- switch also works with char and String
- Use switch for exact values, and if for everything else
- The ternary operator picks between two values in one expression
- Final exercise: a menu calculator that brings it all together
- Review questions
Many questions have more than two answers
if-else chooses between only two roads. But a mark can mean A, B, C, D or F, and a lift button can mean any floor.
else-if chainQuestions in a row about ranges of values: is the mark 90 or more? If not, is it 80 or more?
A question inside a question: is the code correct? If it is, is the balance enough?
switchOne question with exact values: which option did you press on the menu?
A chain runs the first branch whose condition is true, then skips the rest
Three answers from two conditions
Press "Debug line by line" and watch the conditions being checked from the top. Then try n = 5 and n = 0. Why does the last else need no condition?
public class Sign {
public static void main(String[] args) {
int n = -4;
if (n > 0) {
System.out.println("Positive");
} else if (n < 0) {
System.out.println("Negative");
} else {
System.out.println("Zero");
}
}
}Negative
The temperature is 25. More than one condition is true, so what does the program print?
public class Weather {
public static void main(String[] args) {
int temp = 25;
if (temp > 30) {
System.out.println("Hot");
} else if (temp > 20) {
System.out.println("Warm");
} else if (temp > 10) {
System.out.println("Mild");
} else {
System.out.println("Cold");
}
}
}Show the output
Warm
Why? temp > 10 is true too, but it is never even checked. As soon as the chain finds a true condition, it runs that branch and leaves.
The grade calculator
Enter 85, then 90, then 89. Notice the second condition needs no mark < 90: with a mark of 90 or more, the program would never reach it.
import java.util.Scanner;
public class GradeChain {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("Mark: ");
int mark = input.nextInt();
if (mark >= 90) {
System.out.println("A");
} else if (mark >= 80) {
System.out.println("B");
} else if (mark >= 70) {
System.out.println("C");
} else if (mark >= 60) {
System.out.println("D");
} else {
System.out.println("F");
}
}
}The order of conditions changes the result: start with the narrowest
An excellent mark, 95, but the conditions are in reverse order. What does it print?
public class WrongOrder {
public static void main(String[] args) {
int mark = 95;
if (mark >= 60) {
System.out.println("D");
} else if (mark >= 70) {
System.out.println("C");
} else if (mark >= 80) {
System.out.println("B");
} else if (mark >= 90) {
System.out.println("A");
} else {
System.out.println("F");
}
}
}Show the output
D
Why? 95 is more than 60, so the first condition is true and the chain ends. The program can never reach A, whatever the mark: every mark of 90 or more stops at the first condition. The compiler cannot catch this, because it is a logic error, not a writing error.
The correct grade calculator
Read a whole-number mark from 0 to 100, then print the grade: A from 90 up, B from 80, C from 70, D from 60 and F below that. Do not print a prompt.
import java.util.Scanner;
public class GradeCalculator {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
// Read the mark, then write the chain from highest to lowest
}
}100
A
Hint 1
Start with the condition mark >= 90, then work your way down.
Hint 2
The last branch is else with no condition, and it prints F.
Solution
import java.util.Scanner;
public class GradeCalculator {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int mark = input.nextInt();
if (mark >= 90) {
System.out.println("A");
} else if (mark >= 80) {
System.out.println("B");
} else if (mark >= 70) {
System.out.println("C");
} else if (mark >= 60) {
System.out.println("D");
} else {
System.out.println("F");
}
}
}The word else is what ties conditions into one chain
We removed the word else between the statements. What does the program print?
public class SeparateIfs {
public static void main(String[] args) {
int mark = 85;
if (mark >= 90) {
System.out.println("A");
}
if (mark >= 80) {
System.out.println("B");
}
if (mark >= 70) {
System.out.println("C");
} else {
System.out.println("F");
}
}
}Show the output
B C
Why? These are three independent decisions, each checked regardless of the ones before. The last else belongs only to the if right before it, not to all the conditions.
When do you use several separate if statements instead of a chain?
A final else guarantees the variable gets a value
The compiler does not read logic
We know one of the two conditions is always true, but the compiler does not. Change the last branch to reassure it.
public class Result {
public static void main(String[] args) {
int mark = 70;
String result;
if (mark >= 60) {
result = "Pass";
} else if (mark < 60) {
result = "Fail";
}
System.out.println(result);
}
}might not have been initializedShow the fix
public class Result {
public static void main(String[] args) {
int mark = 70;
String result;
if (mark >= 60) {
result = "Pass";
} else {
result = "Fail";
}
System.out.println(result);
}
}Pass
Why? The compiler does not weigh what the two conditions mean, so it sees a possible path where result gets no value. An else covers everything left, so no path remains without an assignment.
A nested if asks its question only after an earlier one succeeds
Cash machine: the code first, then the balance
Press "Debug line by line". Then change pin to 1111, then put it back and change amount to 500. Each wrong answer gets its own message, which one condition with && cannot give.
public class NestedAtm {
public static void main(String[] args) {
int pin = 1234;
int balance = 300;
int amount = 100;
if (pin == 1234) {
if (amount <= balance) {
balance -= amount;
System.out.println("Take your cash");
System.out.println("Balance: " + balance);
} else {
System.out.println("Not enough balance");
}
} else {
System.out.println("Wrong code");
}
}
}Take your cash Balance: 200
A discount that depends on two questions
The purchase is 120 and the customer is a member. Try changing the input to 50 and no, then to 100 and no. Notice printf printing the amount to two decimal places.
import java.util.Scanner;
public class Discount {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
double total = input.nextDouble();
String member = input.next();
double rate;
if (total >= 100) {
if (member.equals("yes")) {
rate = 0.20;
} else {
rate = 0.10;
}
} else {
if (member.equals("yes")) {
rate = 0.05;
} else {
rate = 0.0;
}
}
System.out.printf("Pay: %.2f%n", total - total * rate);
}
}Pay: 96.00
Cash machine withdrawal
The balance is a constant, 500. Read the amount to withdraw as a whole number and check in this order: if it is 0 or less print Invalid amount; if it is above the balance print Not enough balance; if it is not a multiple of 10 print Use multiples of 10. Otherwise print two lines: Take <amount>, then Balance: <remaining>. Do not print a prompt.
import java.util.Scanner;
public class Withdraw {
public static void main(String[] args) {
final int BALANCE = 500;
Scanner input = new Scanner(System.in);
// Read the amount, then check each case in order
}
}100
Take 100 Balance: 400
Hint 1
A multiple leaves no remainder: amount % 10 != 0 means it is not a multiple of 10.
Hint 2
A constant cannot change, so print BALANCE - amount directly.
Solution
import java.util.Scanner;
public class Withdraw {
public static void main(String[] args) {
final int BALANCE = 500;
Scanner input = new Scanner(System.in);
int amount = input.nextInt();
if (amount <= 0) {
System.out.println("Invalid amount");
} else if (amount > BALANCE) {
System.out.println("Not enough balance");
} else if (amount % 10 != 0) {
System.out.println("Use multiples of 10");
} else {
System.out.println("Take " + amount);
System.out.println("Balance: " + (BALANCE - amount));
}
}
}else matches the nearest open if before it, whatever the indentation says
The else looks as if it belongs to the first condition. Does it? x is 5.
public class DanglingElse {
public static void main(String[] args) {
int x = 5;
if (x > 0)
if (x > 10)
System.out.println("Big");
else
System.out.println("Not positive");
System.out.println("End");
}
}Show the output
Not positive End
Why? The compiler ignores indentation and ties else to the nearest if before it, which is x > 10. 5 is positive but not above 10, so Not positive prints, which plainly means the wrong thing.
Braces settle the matter
The same program, but the braces tie else to the first condition. Press "Debug line by line", then try x = -2 and x = 20.
public class ElseWithBraces {
public static void main(String[] args) {
int x = 5;
if (x > 0) {
if (x > 10) {
System.out.println("Big");
}
} else {
System.out.println("Not positive");
}
System.out.println("End");
}
}End
In the code above without braces: which if does else go with?
Leap years: one rule that combines three conditions
| Year | Divides by 4? | Divides by 100? | Divides by 400? | Leap? |
|---|---|---|---|---|
2024 | Yes | No | No | Yes |
2023 | No | No | No | No |
1900 | Yes | Yes | No | No |
2000 | Yes | Yes | Yes | Yes |
The rule: a year is a leap year if it divides by 4 and not by 100, or if it divides by 400.
Name each part of the condition
Boolean variables with clear names make a long condition readable. Try the four years in the table and follow the values with "Debug line by line".
public class LeapParts {
public static void main(String[] args) {
int year = 1900;
boolean by4 = year % 4 == 0;
boolean by100 = year % 100 == 0;
boolean by400 = year % 400 == 0;
System.out.println(by4);
System.out.println(by100);
System.out.println(by400);
System.out.println((by4 && !by100) || by400);
}
}true true false false
Is it a leap year?
Read a year, then print Leap year if it is one, otherwise print Not a leap year. Do not print a prompt.
import java.util.Scanner;
public class LeapYear {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
// Read the year, then apply the rule
}
}2024
Leap year
Hint 1
"Divides by 4" means year % 4 == 0.
Hint 2
(year % 4 == 0 && year % 100 != 0) || year % 400 == 0, and the parentheses make it clear.
Solution
import java.util.Scanner;
public class LeapYear {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int year = input.nextInt();
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
System.out.println("Leap year");
} else {
System.out.println("Not a leap year");
}
}
}switch jumps to the case equal to the value
switch (value) {
case 1:
// runs when value is 1
break; // leave the switch
case 2:
// runs when value is 2
break;
default:
// runs when no case matches
}case takes a fixed value and a colon, and the lines after it need no braces.Name of the day
Day 1 is Sunday. Press "Debug line by line" to see the direct jump to case 3. Then try day = 9 to see default at work.
public class DayName {
public static void main(String[] args) {
int day = 3;
switch (day) {
case 1:
System.out.println("Sunday");
break;
case 2:
System.out.println("Monday");
break;
case 3:
System.out.println("Tuesday");
break;
case 4:
System.out.println("Wednesday");
break;
case 5:
System.out.println("Thursday");
break;
default:
System.out.println("Weekend or not a day");
}
System.out.println("Done");
}
}Tuesday Done
When does default run?
default is like the final else of a chain: it takes everything that did not match. It is optional, but it protects the program from unexpected values.Without break the program carries on into the next cases
Look carefully at where break appears. n is 2. What does it print?
public class FallThrough {
public static void main(String[] args) {
int n = 2;
switch (n) {
case 1:
System.out.println("One");
case 2:
System.out.println("Two");
case 3:
System.out.println("Three");
break;
case 4:
System.out.println("Four");
default:
System.out.println("Other");
}
}
}Show the output
Two Three
Why? The program jumps to case 2, then keeps moving down line by line without checking any other case until it hits break. This behaviour is called fall-through.
A wrong price from a missing break
Size 1 costs 5, yet the program prints 10. Press "Debug line by line" and watch price change three times. Then add break where it belongs so the program prints Price: 5.
public class SizePrice {
public static void main(String[] args) {
int size = 1;
int price = 0;
switch (size) {
case 1:
price = 5;
case 2:
price = 8;
case 3:
price = 10;
break;
default:
price = 0;
}
System.out.println("Price: " + price);
}
}Price: 10
Deliberate fall-through groups several cases into one answer
Days in the month
Seven months have 31 days. Stack their cases on top of each other, and they all fall to the same line. Press "Debug line by line", then change month to 1, then 2, then 13.
public class DaysInMonth {
public static void main(String[] args) {
int month = 4;
int days;
switch (month) {
case 1: case 3: case 5: case 7:
case 8: case 10: case 12:
days = 31;
break;
case 4: case 6: case 9: case 11:
days = 30;
break;
case 2:
days = 28;
break;
default:
days = 0;
}
System.out.println("Days: " + days);
}
}Days: 30
Days in the month, leap years included
Read the month number, then the year. Print only the number of days in that month, with February having 29 days in a leap year. If the month is not from 1 to 12, print Invalid month. Do not print a prompt.
import java.util.Scanner;
public class MonthDays {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int month = input.nextInt();
int year = input.nextInt();
// Use switch on month; put the leap-year check under case 2
}
}1 2023
31
Hint 1
Inside case 2: write an if-else using the leap-year rule, then break.
Hint 2
Put the error message in default, and print the number of days inside each case before break.
Solution
import java.util.Scanner;
public class MonthDays {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int month = input.nextInt();
int year = input.nextInt();
switch (month) {
case 1: case 3: case 5: case 7:
case 8: case 10: case 12:
System.out.println(31);
break;
case 4: case 6: case 9: case 11:
System.out.println(30);
break;
case 2:
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
System.out.println(29);
} else {
System.out.println(28);
}
break;
default:
System.out.println("Invalid month");
}
}
}switch also works with char and String
Describing a grade with char
A char value is written inside single quotes. Change grade to 'a', then to 'Z'. Notice how case 'A': case 'a': groups the capital and small letters.
public class GradeWords {
public static void main(String[] args) {
char grade = 'B';
switch (grade) {
case 'A': case 'a':
System.out.println("Excellent");
break;
case 'B': case 'b':
System.out.println("Very good");
break;
case 'C': case 'c':
System.out.println("Good");
break;
default:
System.out.println("Unknown grade");
}
}
}Very good
A text command from the user
switch works with String since Java 7, and it compares using equals, so it works with text read by Scanner. Change the input to stop, then to Start.
import java.util.Scanner;
public class Commands {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String command = input.next();
switch (command) {
case "start":
System.out.println("Starting");
break;
case "stop":
System.out.println("Stopping");
break;
default:
System.out.println("Unknown command");
}
}
}Starting
The input is Yes with a capital letter. What does it print?
import java.util.Scanner;
public class Confirm {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String reply = input.next();
switch (reply) {
case "yes":
System.out.println("Confirmed");
break;
case "no":
System.out.println("Cancelled");
break;
default:
System.out.println("Please answer yes or no");
}
}
}Show the output
Please answer yes or no
Why? switch on strings compares characters exactly, as equals does, and Yes is not yes.
Ticket price
Read the ticket type as one word and use switch: child costs 5, adult costs 10 and senior costs 7. Print Price: <price>, and for any other word print Unknown ticket. Do not print a prompt.
import java.util.Scanner;
public class Ticket {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String type = input.next();
// Write the switch here
}
}child
Price: 5
Hint 1
Do not forget break after each case, or the program prints several prices.
Hint 2
Strings in a case go inside double quotes: case "child":.
Solution
import java.util.Scanner;
public class Ticket {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String type = input.next();
switch (type) {
case "child":
System.out.println("Price: 5");
break;
case "adult":
System.out.println("Price: 10");
break;
case "senior":
System.out.println("Price: 7");
break;
default:
System.out.println("Unknown ticket");
}
}
}Use switch for exact values, and if for everything else
| Situation | Best choice | Why |
|---|---|---|
| A number from a menu | switch | Exact values of one variable |
| A grade from a mark | if | Ranges, not exact values |
| An age and a card together | if | More than one variable |
A double temperature | if | switch does not take double |
A command character such as + or - | switch | Exact char values |
A condition where a value belongs
A case does not take a condition, only a fixed value. Rewrite the program with if-else so it prints A.
public class CaseRange {
public static void main(String[] args) {
int mark = 95;
switch (mark) {
case mark >= 90:
System.out.println("A");
break;
default:
System.out.println("Not A");
}
}
}incompatible types: boolean cannot be converted to intShow the fix
public class CaseRange {
public static void main(String[] args) {
int mark = 95;
if (mark >= 90) {
System.out.println("A");
} else {
System.out.println("Not A");
}
}
}A
Why? switch compares the int value mark with each case value. mark >= 90 is a boolean, so it cannot be compared with an int. That is why the message talks about types. Ranges are the job of if.
Two cases with the same value
The programmer meant the second case to print Friday for number 6. Fix the number.
public class DuplicateCase {
public static void main(String[] args) {
int day = 6;
switch (day) {
case 5:
System.out.println("Thursday");
break;
case 5:
System.out.println("Friday");
break;
default:
System.out.println("Other");
}
}
}duplicate case labelShow the fix
public class DuplicateCase {
public static void main(String[] args) {
int day = 6;
switch (day) {
case 5:
System.out.println("Thursday");
break;
case 6:
System.out.println("Friday");
break;
default:
System.out.println("Other");
}
}
}Friday
Why? If the compiler allowed two equal cases, nobody could say which one the program jumps to. So every case value must be unique inside a switch.
Which problem does not suit switch?
switch matches only exact values. The other problems all have known, countable values.The ternary operator picks between two values in one expression
result = condition ? valueIfTrue : valueIfFalse;One line instead of five
Change mark to 45 and predict the output. Then rewrite the third line with if-else and compare the two ways.
public class Ternary {
public static void main(String[] args) {
int mark = 72;
int bonus = mark >= 70 ? 5 : 0;
String result = mark >= 60 ? "Pass" : "Fail";
System.out.println(result);
System.out.println("Bonus: " + bonus);
}
}Pass Bonus: 5
It can go straight inside a print, in parentheses. What does this print?
public class TernaryPrint {
public static void main(String[] args) {
int x = 7;
System.out.println(x + " is " + (x % 2 == 0 ? "even" : "odd"));
int max = x > 10 ? x : 10;
System.out.println(max);
}
}Show the output
7 is odd 10
Why? 7 divided by 2 leaves 1, so the condition is false and we take "odd". In the second line 7 is not above 10, so we take 10.
Price by age
Read the age, then use the ternary operator to give price the value 5 if the age is under 12, otherwise 10. Print Price: <price>. Do not print a prompt.
import java.util.Scanner;
public class AgePrice {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int age = input.nextInt();
// int price = ... ? ... : ...;
}
}5
Price: 5
Hint 1
The condition is age < 12, and it comes before the ?.
Solution
import java.util.Scanner;
public class AgePrice {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int age = input.nextInt();
int price = age < 12 ? 5 : 10;
System.out.println("Price: " + price);
}
}Final exercise: a menu calculator that brings it all together
- Print the menuFour lines, exactly as in the exercise text.
- Read three valuesThe choice as an
int, then the two numbers asdouble. - Choose with
switchOne case for each operation, anddefaultfor an invalid choice. - Guard the division with
ifInside the division case, check that the second number is not zero.
The calculator
Print these four lines: 1) Add, 2) Subtract, 3) Multiply and 4) Divide. Then read the choice and the two numbers. Print Result: followed by the answer to two decimal places using printf. When dividing by zero print Cannot divide by zero, and for any other choice print Invalid choice.
import java.util.Scanner;
public class MenuCalculator {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("1) Add");
System.out.println("2) Subtract");
System.out.println("3) Multiply");
System.out.println("4) Divide");
// Read the choice and the two numbers, then use switch
}
}1 5 3
1) Add 2) Subtract 3) Multiply 4) Divide Result: 8.00
Hint 1
Dividing a double by zero does not throw an exception; it gives Infinity, so check for it yourself with if.
Hint 2
System.out.printf("Result: %.2f%n", a + b);, followed by break.
Solution
import java.util.Scanner;
public class MenuCalculator {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("1) Add");
System.out.println("2) Subtract");
System.out.println("3) Multiply");
System.out.println("4) Divide");
int choice = input.nextInt();
double a = input.nextDouble();
double b = input.nextDouble();
switch (choice) {
case 1:
System.out.printf("Result: %.2f%n", a + b);
break;
case 2:
System.out.printf("Result: %.2f%n", a - b);
break;
case 3:
System.out.printf("Result: %.2f%n", a * b);
break;
case 4:
if (b != 0) {
System.out.printf("Result: %.2f%n", a / b);
} else {
System.out.println("Cannot divide by zero");
}
break;
default:
System.out.println("Invalid choice");
}
}
}Review questions
At most how many branches run in an if, else if, else chain?
else and no condition is true, no branch runs at all.What happens if you forget break in a case?
case is only an entry point. Execution stops only at break or at the end of the switch.Which type does switch not accept in Java 8?
switch does not accept them.Predict: a chain, then a switch, then a ternary.
public class ReviewMixed {
public static void main(String[] args) {
int n = 15;
if (n > 20) {
System.out.println("High");
} else if (n > 10) {
System.out.println("Mid");
} else {
System.out.println("Low");
}
switch (n % 4) {
case 3:
System.out.println("Three");
case 2:
System.out.println("Two");
break;
default:
System.out.println("Other");
}
System.out.println(n > 10 ? "Yes" : "No");
}
}Show the output
Mid Three Two Yes
Why? 15 is above 10 but not above 20, so Mid prints. 15 divided by 4 leaves 3, so the program jumps to case 3 and falls into case 2 up to break. Then the ternary prints Yes.