Making decisions: if-else and switch
Branching, the shape of conditions, and when switch reads better than a chain of ifs.
Finished reading?
Mark this session so you can track where you are.
Branching, the shape of conditions, and when switch reads better than a chain of ifs.
Finished reading?
Mark this session so you can track where you are.
Until now every program you wrote ran straight down the page, top to bottom, every line every time. Real programs are not like that. They look at what is true right now and choose what to do next. That choosing is called a decision, and this session is about the handful of ways Java lets you write one.
if that runs a block of code only when a condition is true.else if and else, and predict which branch runs.boolean expression, not a magic phrase.cond ? a : b when you are choosing a value, not an action.switch statement and explain fall-through and break.<, <=, >, >=, ==, !=) and the logical operators (&&, ||, !), all of which produce a boolean. We use only those plus variables and types. No loops, arrays, or objects appear here yet.The smallest decision in Java is the if statement. You give it a condition in parentheses and a block of code in braces. If the condition is true, the block runs. If it is false, the block is skipped and the program carries on below it.
public class Main { public static void main(String[] args) { int temperature = 35; if (temperature > 30) { System.out.println("It is hot today."); } System.out.println("Have a good day."); }}It is hot today. Have a good day.
Read it slowly. temperature is 35, so temperature > 30 is true, and the line inside the braces runs. The last line is outside the braces, so it always runs no matter what. Change 35 to 20 and the first message disappears while the second stays. The braces mark exactly which lines belong to the decision.
The thing inside the parentheses is not a sentence Java reads in English. It is an expression that evaluates to a single
boolean, eithertrueorfalse. Theifonly cares about that one value.
if (x > 0) System.out.println("positive");. It works, but it invites a classic mistake: you add a second line later, think it is part of the if, and it is not. Always write the braces. The cost is two characters and the payoff is never being surprised.An ifon its own answers a yes-or-no question. To say “do this, otherwise do that” you add an else. The else block runs exactly when the if condition was false. Together they form a fork in the road: one branch or the other runs, never both, never neither.
public class Main { public static void main(String[] args) { int age = 16; if (age >= 18) { System.out.println("You may vote."); } else { System.out.println("Too young to vote."); } }}Too young to vote.
Often you have more than two cases. For that you stack conditions with else if. Java checks each condition from the top down and runs the block for the first one that is true. As soon as one matches, the rest are skipped entirely. A final bare else catches everything that fell through.
public class Main { public static void main(String[] args) { int n = -7; if (n < 0) { System.out.println(n + " is negative"); } else if (n == 0) { System.out.println(n + " is zero"); } else { System.out.println(n + " is positive"); } }}-7 is negative
Press Run and step through. Watch the order: Java tests n < 0 first. Because n is -7, that is true, so the negative branch runs and the other two are never even looked at. The output is -7 is negative. Change n to 0 and the first test fails, the second succeeds, and you get the zero branch. The order of the tests is the order of the checking.
In an
if/else if/elsechain, exactly one branch runs: the first whose condition is true, or the finalelseif none of them are. Order matters, because checking stops at the first match.
Let us build something real with the chain. We have a numeric score from 0 to 100 and we want a letter grade. The thresholds are the usual ones: 90 and up is an A, 80 and up a B, 70 and up a C, 60 and up a D, anything lower an F. Notice how each branch can assume the higher ones already failed, so we only need a single lower bound per branch.
public class Main { public static void main(String[] args) { int score = 73; char grade; if (score >= 90) { grade = 'A'; } else if (score >= 80) { grade = 'B'; } else if (score >= 70) { grade = 'C'; } else if (score >= 60) { grade = 'D'; } else { grade = 'F'; } System.out.println("Score " + score + " earns grade " + grade); }}Score 73 earns grade C
Trace it. With score at 73: 73 >= 90 is false, 73 >= 80 is false, 73 >= 70 is true, so grade becomes 'C' and the chain ends. The program prints Score 73 earns grade C. Try a few values yourself: 90 gives A, 59 gives F, and the boundary 80 gives B because we used >=, not >.
80 a B or a C? With score >= 80 it is a B; with score > 80 it would slip down to a C. Whenever you write a threshold, pick one exact value sitting on the line and trace it by hand. The boundary is the case worth testing.Because a condition is only an expression that produces a boolean, everything you learned about logical operators applies directly. You can join several tests into one with && (and), || (or), and ! (not). This keeps a chain short when a branch really depends on two facts at once.
public class Main { public static void main(String[] args) { int age = 65; boolean isMember = true; if (age >= 60 && isMember) { System.out.println("Senior member discount"); } else if (age >= 60 || isMember) { System.out.println("Partial discount"); } else { System.out.println("No discount"); } }}Senior member discount
Here age >= 60 is true and isMember is already a boolean that is true, so age >= 60 && isMember is true and the first branch wins: Senior member discount. Notice isMember needs no comparison. It is already a yes-or-no value, so it can stand on its own inside the parentheses.
= where you meant ==. = puts a value into a variable; == asks whether two values are equal. Writing if (isMember = true) does not test isMember, it sets it to true and then uses that. Java catches this for plain numbers (if (x = 5) will not compile, because 5 is an int not a boolean), but with a boolean variable it slips through. Always write == for a comparison.You can put an if inside another if. The inner one is only reached when the outer condition is already true, so together they ask “is this true, and within that, is this other thing true?”. A classic use is finding the largest of three numbers.
public class Main { public static void main(String[] args) { double a = -1.0; double b = 4.5; double c = -5.3; double largest; if (a >= b) { if (a >= c) { largest = a; } else { largest = c; } } else { if (b >= c) { largest = b; } else { largest = c; } } System.out.println("Largest is " + largest); }}Largest is 4.5
Read it as a tree. The outer if (a >= b) splits the problem in two. On the “yes” side we already know a beats b, so we only need to compare aagainst c. On the “no” side b beats a, so we compare bagainst c. Here b is the largest, so it prints Largest is 4.5.
&& instead of nesting. Nesting reads best when the inner question only makes sense once the outer one is settled. If you find yourself nested three or four deep, that is usually a sign to step back and simplify, perhaps with combined conditions or, later, with methods.Sometimes a decision is not about which action to take, but which value to store. You want max to be whichever of two numbers is larger, or a label to be one word or another. Writing a four-line if / else just to set one variable feels heavy. Java has a one-line form for exactly this: the ternary operator, written condition ? a : b.
Read it as a question. The part before the ? is the condition. If it is true, the whole expression becomes the value after the ?. If it is false, it becomes the value after the :. It is the only operator in Java that takes three parts, which is where the name ternary comes from.
public class Main { public static void main(String[] args) { int age = 17; String status = age >= 18 ? "adult" : "minor"; System.out.println("At " + age + " you are a " + status); int x = -4; int size = x < 0 ? -x : x; System.out.println("The size of " + x + " is " + size); }}At 17 you are a minor The size of -4 is 4
Two uses, side by side. In the first, age >= 18 is false (age is 17), so status becomes "minor" and we print At 17 you are a minor. In the second, x < 0 is true, so size becomes -x, which is 4, and we print The size of -4 is 4. That second line is the absolute value, written in a single expression.
= or inside a larger expression. It is not a place to run actions. If a branch needs to print, update several variables, or do real work, use a full if / else. The ternary shines for the small case “set this one thing to A or B” and becomes hard to read the moment you try to stretch it further.There is a special shape of decision where a single variable is checked against a list of exact values: if it is 1 do this, if it is 2 do that, and so on. You can write that as a long else if chain, but it repeats the variable on every line. The switch statement is built for this one shape. You name the value once, then list the cases.
if, else, and the ternary, but it does not run switch. So the next two examples have no Run button. Read them carefully and check the stated output by tracing in your head. We are honest about what Run can and cannot do, and a fake trace would teach you the wrong thing.public class Main { public static void main(String[] args) { int day = 3; switch (day) { case 1: System.out.println("Monday"); break; case 2: System.out.println("Tuesday"); break; case 3: System.out.println("Wednesday"); break; default: System.out.println("Another day"); } }}Wednesday
Trace it. day is 3, so control jumps to case 3, prints Wednesday, and the break ends the switch. The default label is the catch-all, like the final else in a chain: it runs when no case matched. If day had been 9, you would see Another day.
That break after each case is not decoration. A switch finds the matching case and then keeps running downward through the following cases until it hits a break or the end. That behavior is called fall-through. The break stops it. Forgetting one is the classic switch bug, because the program quietly does more than you intended.
Fall-through is not always a mistake. When you deliberately want several values to share one block, you stack the case labels with no code between them and let them fall into a single body. Here both 'A' and 'B' lead to the same message.
public class Main { public static void main(String[] args) { char grade = 'B'; switch (grade) { case 'A': case 'B': System.out.println("Well done"); break; case 'C': System.out.println("Solid pass"); break; default: System.out.println("Keep working"); } }}Well done
With grade equal to 'B', control lands on case 'B'. There is no code directly under it, so it falls into the shared body below case 'A' and prints Well done, then break ends the switch. Stacking labels this way is the intended, readable use of fall-through.
&&, ||, anything.score >= 90.1, 2, 3.break.if that gets asked again and again. Everything you learned about boolean conditions here carries straight over.if (cond) { } runs a block when cond is a boolean that is true.else handles the false case; else if adds more conditions, checked top to bottom, first match wins.boolean, so &&, ||, and ! all fit inside it.cond ? a : b is a one-line way to choose a value, not an action.switch matches one value against exact cases; break stops fall-through, default is the catch-all.== to compare and = to assign. Mixing them up is the bug to watch for.Try each one yourself first, then open the answer.
if (...) evaluate to, and why is if (temperature) for an int temperature an error in Java?score >= 90, then >= 80, then >= 70, and so on. What grade does a score of 95 get if you reorder the chain to check score >= 60 first?String label; if (n % 2 == 0) label = "even"; else label = "odd";switch, suppose the break after case 1 is deleted and day is 1. What gets printed, and what is this behavior called?if (x = 5) fail to compile, while if (done = true) compiles when done is a boolean?Take these away. They continue exactly what we just did.
int hour from 0 to 23 and prints "Good morning", "Good afternoon", or "Good evening" using an if / else if / else chain. Decide your own boundaries and test the value sitting exactly on each one.int larger whichever of two int variables a and b is greater. Then print it. Confirm it works when a is bigger, when b is bigger, and when they are equal.switch on an int month from 1 to 12 that prints the season. Group the months that share a season by stacking case labels with one shared body, the way the grade example stacked 'A' and 'B'. Mark clearly where each break goes. Do not run it; trace month at 3 and at 12 by hand.18 who is a member should get "Junior member discount". Where in the chain must this branch go so it is reached, and why? Write one sentence explaining the ordering before you change the code.