Topics › Basics › Things to Know in C++/Java/Python or any language
If ElseIf
Java source ↗ Edit this article ↗
Jump to code:
What this covers
An if statement lets a program take one path or another depending on a condition. That sounds too simple to deserve an article, yet a large share of wrong answers on easy problems come from branches: a boundary tested with > instead of >=, two conditions checked in the wrong order, a condition that reads an array index before checking that the index is valid. This article walks through the forms of if in Java, when to chain them with else if, when to nest, and how to reason about the order of tests.
The grammar is small. if (condition) statement runs the statement when the condition, a boolean expression, is true. An optional else provides the other path. Writing else if is not a separate keyword; it is an else whose statement happens to be another if, which is what makes chains work.
The else-if chain
When several conditions are mutually exclusive and exactly one outcome should be chosen, chain them. Java evaluates the conditions top to bottom and runs the body of the first one that is true; everything below it is skipped, even if it would also have been true.
static String grade(int marks) {
if (marks >= 90) return "A";
else if (marks >= 80) return "B";
else if (marks >= 70) return "C";
else if (marks >= 60) return "D";
else return "F";
}
grade(95) is A, and the marks >= 80 test is never evaluated for it. This is why the chain can use simple one-sided tests: by the time execution reaches marks >= 80, we already know marks < 90. The final else is the catch-all; every path returns, so the compiler is satisfied that the method always produces a value. Boundaries deserve a test each: grade(90) must be A, grade(60) must be D, grade(59) must be F.
Nesting for rules with exceptions
Some rules are naturally layered. A year is a leap year if divisible by 4, except century years, except that every 400th year is a leap year after all. Nesting follows the wording:
static boolean isLeapYear(int year) {
if (year % 4 != 0) {
return false;
} else if (year % 100 != 0) {
return true;
} else {
return year % 400 == 0;
}
}
Read it as a funnel. Years not divisible by 4 drop out first. Of those that remain, the ones not divisible by 100 are leap years. Only centuries reach the last branch, and there year % 400 == 0 is itself a boolean, so it is returned directly instead of writing if (...) return true; else return false;. 1900 is not a leap year and 2000 is, which is the pair every implementation should be tested on.
Order matters
When conditions overlap, the order of tests decides the answer. FizzBuzz is the standard illustration: a multiple of 15 is also a multiple of 3 and of 5, so the test for 15 must come first or it can never win.
static String fizzBuzz(int n) {
if (n % 15 == 0) return "FizzBuzz";
if (n % 3 == 0) return "Fizz";
if (n % 5 == 0) return "Buzz";
return String.valueOf(n);
}
Here the else keywords are omitted because each branch returns; a return ends the method, so the following if statements are only reached when the earlier ones failed. This “guard clause” style reads well when each case is a complete answer. The general rule: put the most specific condition first and the most general last.
Several independent ifs versus one chain
Two if statements in a row without else are both evaluated, and both can run. That is sometimes exactly what you want: a running maximum is updated by each value that beats it.
static int maxOfThree(int a, int b, int c) {
int best = a;
if (b > best) best = b;
if (c > best) best = c;
return best;
}
The same result with a chain requires comparing every candidate against all the others, because exactly one branch may run:
static int maxOfThreeChain(int a, int b, int c) {
if (a >= b && a >= c) return a;
else if (b >= c) return b;
else return c;
}
Both are correct, and the tests feed them identical inputs including ties and all-negative triples. The first scales to n values with a loop; the second does not, which is a hint about which habit to build.
The ternary operator
condition ? valueIfTrue : valueIfFalse is an expression, so it can sit on the right of an assignment or inside a return. It is ideal for choosing between two values; nested one level deep it is still readable, beyond that it is not.
static String sign(int x) {
return x > 0 ? "positive" : (x < 0 ? "negative" : "zero");
}
The parentheses around the inner ternary are optional (the operator is right-associative) but they make the grouping obvious to a reader.
Short-circuit evaluation
&& and || evaluate their right operand only when the left one has not already decided the result. For &&, a false on the left means the whole expression is false, so the right side is skipped. This is not a performance detail; it is a correctness tool, because it lets you put a guard before an operation that would otherwise throw.
static boolean isPositiveAt(int[] a, int i) {
return i >= 0 && i < a.length && a[i] > 0;
}
For i = -1 the first test fails and a[i] is never evaluated, so there is no ArrayIndexOutOfBoundsException. Reversing the order to a[i] > 0 && i >= 0 would crash. The same technique protects against null: s != null && s.isEmpty().
Compound conditions
Conditions combine with && (both), || (either) and ! (not). Inside a branch you can nest a further if when a sub-rule applies only there:
static String ticketPrice(int age, boolean student) {
if (age < 5 || age >= 65) {
return "free";
} else if (age < 18) {
return "child";
} else {
if (student) return "student";
return "adult";
}
}
Note that student is a boolean already, so if (student) is the whole test; if (student == true) says the same thing with more room for typos. Also note that the student discount is checked only for adults: a 17-year-old student is a child, because the chain decided that before the nested if was reached.
Edge cases and pitfalls
- Boundaries.
>versus>=is the most frequent bug in branching code. Decide what happens exactly at each threshold and write a test for it. - Order of overlapping conditions. Test the most specific condition first (
% 15before% 3). When conditions are mutually exclusive the order does not change the answer, but it still affects speed and readability. - Dangling else. Without braces, an
elsebinds to the nearestif. Use braces whenever anifcontains anotherif. - Assignment in a condition.
if (x = 5)does not compile forint, butif (flag = true)compiles for abooleanand silently assigns. Writeif (flag). - Comparing strings.
if (s == "yes")compares references. Uses.equals("yes"), or"yes".equals(s)whensmay benull. - Reading before guarding. Put range and null checks on the left of
&&so that short-circuiting protects the risky operation on the right. - Missing final else. A chain that returns in every branch needs a final
else(or a return after the chain) or the compiler reports a missing return.
Related problems
- Switch Case is the cleaner tool when a chain compares one value against many constants.
- For loops combines branching with repetition, where
breakandcontinueare conditional jumps. - Cpp Basics covers the comparison and arithmetic operators used in every condition here.