Skip to content

Control Flow — if, else, switch, Ternary Operator, and Pattern Matching

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about Control Flow. We cover key concepts, practical examples, and best practices to help you master this topic.

Control flow statements in Java direct the order of execution based on conditions, with if/else, switch expressions, the ternary operator, and pattern matching. Without control flow, every program would execute line-by-line from start to finish — control flow gives programs the ability to make decisions, react to data, and branch into different execution paths.

What You'll Learn

  • if/else chains and best practices
  • switch statements vs switch expressions (Java 14+)
  • Ternary operator for compact conditionals
  • Pattern matching for instanceof (Java 16+) and switch (Java 17+, preview)

Why It Matters

Control flow is the backbone of logic in every program. Choosing the right construct — if for complex conditions, switch for many discrete values, ternary for simple assignments — makes code more readable and less error-prone.

Real-World Use

A REST API controller uses if/else for validation, a shopping cart uses switch to apply different discount codes, and a configuration parser uses pattern matching to handle different JSON node types.


The if Statement

The simplest form tests a single condition:

int temperature = 30;
if (temperature > 25) {
    System.out.println("It is a hot day.");
}

Java requires parentheses around the condition and braces for the body. The braces are optional for single statements but strongly recommended for readability and maintenance.

if/else and else if

int score = 85;
if (score >= 90) {
    System.out.println("Grade: A");
} else if (score >= 80) {
    System.out.println("Grade: B");
} else if (score >= 70) {
    System.out.println("Grade: C");
} else {
    System.out.println("Grade: F");
}

The conditions are evaluated top-down. Once one condition is true, the corresponding block executes and the rest are skipped.

The Ternary Operator

For simple binary assignments, the ternary operator (?:) is more concise:

int age = 20;
String status = (age >= 18) ? "Adult" : "Minor";
System.out.println(status); // Adult

Nested ternaries are possible but usually a bad idea:

String result = (a > b) ? "a > b" : (a < b) ? "a < b" : "equal";

Prefer if/else for more than one level of nesting.

switch Statement (Traditional)

int dayOfWeek = 3;
String dayName;
switch (dayOfWeek) {
    case 1:
        dayName = "Monday";
        break;
    case 2:
        dayName = "Tuesday";
        break;
    case 3:
        dayName = "Wednesday";
        break;
    case 4:
        dayName = "Thursday";
        break;
    case 5:
        dayName = "Friday";
        break;
    default:
        dayName = "Weekend";
        break;
}

Forgetting break causes fall-through: execution continues into the next case. This is a common source of bugs.

switch Expression (Java 14+)

The switch expression form is more concise and safer — no fall-through:

int dayOfWeek = 3;
String dayName = switch (dayOfWeek) {
    case 1 -> "Monday";
    case 2 -> "Tuesday";
    case 3 -> "Wednesday";
    case 4 -> "Thursday";
    case 5 -> "Friday";
    default -> "Weekend";
};
System.out.println(dayName); // Wednesday

Arrow (->) cases have no fall-through. If you need multiple statements per arm, use a block:

String result = switch (dayOfWeek) {
    case 1 -> {
        System.out.println("First day");
        yield "Monday";
    }
    default -> "Unknown";
};

The yield keyword returns a value from a block arm. Switch expressions must be exhaustive — you must cover all possible values or include a default.

Switching on Enums

enum Color { RED, GREEN, BLUE }

Color c = Color.RED;
String hex = switch (c) {
    case RED -> "#FF0000";
    case GREEN -> "#00FF00";
    case BLUE -> "#0000FF";
    // no default needed: all cases covered
};

Pattern Matching for instanceof (Java 16+)

The old pattern required a separate variable declaration after instanceof:

// Old way
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

With pattern matching, the variable is declared inline:

// New way (Java 16+)
if (obj instanceof String s) {
    System.out.println(s.length());
}

The variable s is scoped to the if block and is only accessible if the instanceof check succeeds.

Pattern Matching for switch (Java 17+, Preview in 17, Standardized in 21)

Object obj = "Hello";
String result = switch (obj) {
    case Integer i -> "Integer: " + i;
    case String s  -> "String of length " + s.length();
    case null      -> "null value";
    default        -> "Unknown type";
};

Pattern matching in switch allows you to match both the type and optionally add a guard (when clause):

Object obj = 42;
String result = switch (obj) {
    case Integer i when i > 0 -> "Positive integer: " + i;
    case Integer i            -> "Non-positive integer: " + i;
    case String s             -> "String: " + s;
    default                   -> "Other";
};

Pattern matching eliminates the tedium of cascading if/else if chains with instanceof checks.

Common Mistakes

  1. Forgetting break in traditional switch. This causes fall-through. Use switch expressions (arrow syntax) to avoid this entirely.
  2. Using = instead of == in conditions. if (x = 5) does not compile in Java (unlike C/C++) because it requires a boolean.
  3. Omitting curly braces for single-statement blocks. This leads to bugs when adding lines later:
    if (condition)
        doSomething();     // only this is conditional
        doSomethingElse(); // always executes
    
  4. Not covering all cases in a switch expression. Switch expressions must be exhaustive. The compiler enforces this.
  5. Using switch on null. Traditional switch throws NullPointerException if the selector is null. Modern switch (pattern matching) can handle null with case null.

Practice Questions

1. What is the difference between switch statement and switch expression?
A statement performs actions; an expression produces a value. Switch expressions use -> (no fall-through), require exhaustiveness, and use yield to return values from blocks.

2. What does this code print?

int x = 5;
if (x = 10) {
    System.out.println("x is 10");
}

It does not compile. x = 10 is an assignment, not a comparison, and it produces an int, not a boolean.

3. How does pattern matching in switch differ from instanceof in if?
instanceof in if checks one type at a time. Switch pattern matching can check multiple types in separate cases, including null handling and guard conditions.

4. Why does this code produce a null pointer?

Integer val = null;
switch (val) {
    case 1 -> System.out.println("one");
}

Traditional switch unboxes the Integer to int, causing a NullPointerException. Use a default case or pattern matching with case null.

5. Is the ternary operator always preferable to if/else?
No. Ternary is best for simple assignments. For complex logic or side effects, use if/else for clarity.

Challenge Question:
Write a program that uses pattern matching in switch to Process a list of mixed objects — String, Integer, Double, and null. For each, print a formatted description. Use a guard to handle negative numbers differently.

FAQ

What is fall-through in a switch statement?

Fall-through happens when a case block does not end with break, causing execution to continue into the next case. This was inherited from C and is sometimes used intentionally (e.g., multiple cases sharing the same handler) but often causes bugs.

When should I use `switch` vs `if`/`else`?

Use switch (especially switch expressions) when branching on a single value with many discrete cases. Use if/else for complex boolean conditions, range checks, or when the conditions are unrelated.

Can I use `switch` with `String`?

Yes, Java 7+ supports String in switch. The comparison is case-sensitive and uses the String.equals() method internally. You can also use switch expressions with strings.

What is a guard condition in pattern matching?

A guard (using when) adds an additional boolean condition to a pattern. For example, case Integer i when i > 0 -> matches only positive integers. Guards refine pattern matches beyond type checking.

Why does the switch expression need to be exhaustive?

Switch expressions produce a value. If some cases are not covered, the compiler cannot guarantee the expression has a value in all scenarios. Exhaustiveness ensures runtime safety. The default case covers any unlisted value.

Mini Project

Write a program Calculator.java that:

  1. Reads two numbers and an operator (+, -, *, /, %) from command-line arguments
  2. Uses a switch expression to perform the operation
  3. Handles division by zero with an if check
  4. Uses pattern matching with instanceof to validate that arguments are numeric
  5. Formats output with printf

Example run: java Calculator 10 + 3 should print 10 + 3 = 13. Test edge cases: division by zero, non-numeric input, and missing arguments.

What's Next

Control flow lets you make decisions, but real programs need repetition. Lesson 7 covers loops — for, while, do-while, and the enhanced for-each loop — along with break, continue, and labeled loops for fine-grained flow control.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro