تعبيرات switch (Java الحديثة)
في هذا الدرس 11 موضوعات
هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.
Lesson Overview
The classic switch has two weaknesses: a forgotten break makes it fall through, and to get a value out of it you must assign a variable in every branch. Modern Java fixes both with the arrow form case X -> ..., which never falls through, and with the switch expression, a switch that produces a value you can store directly.
Think of the junction again. The classic switch is a junction without barriers; the arrow form builds a barrier at every exit automatically. And a switch expression is a junction that hands you a ticket at the exit with the answer written on it.
This lesson builds on Lesson 3 (labels, break, default, fall-through) and on Module 9 (variables and types). It matters for your career because switch expressions are the foundation for matching on types in later Java versions, which the course reaches in the Junior level.
Learning Objectives
By the end of this lesson you will be able to:
- Write a switch expression that returns a value and assign it to a variable.
- Use the arrow form, including several labels in one
case, as a statement and as an expression. - Explain why the arrow form cannot fall through and needs no
break. - Explain why a switch expression needs a
default(or otherwise full coverage) and read the compiler error when it lacks one. - Use a block with
yieldwhen one case needs several statements. - Convert a classic switch to a switch expression, and decide which to use.
Written Explanation
The arrow form
switch (day) {
case 6, 7 -> System.out.println("Weekend");
default -> System.out.println("Weekday");
}Differences from the classic form:
- Each label is followed by
->and then one action: an expression, a block in braces, or athrow(which you will learn later). - Only that action runs. There is no fall-through and no
break. - Several constants can share a rule with commas:
case 6, 7 ->. You no longer stack empty labels.
You cannot mix the two styles in one switch. A case 1 -> next to a case 2: is rejected by the compiler (Common Mistakes).
The switch expression
A switch can be used as an expression, meaning it has a value, so it can sit on the right side of an assignment:
String type = switch (day) {
case 6, 7 -> "Weekend";
default -> "Weekday";
};Notice the semicolon after the closing brace: this is one statement, String type = ...;. Each rule's right-hand side is the value for that case, and all values must be compatible with the type you assign to.
It must cover every case
A statement may do nothing if no label matches. An expression cannot: it must always produce a value. So the compiler requires a switch expression to be exhaustive: every possible selector value must be handled. For an int or a String that means a default, because there are far too many values to list. If you forget it, the compiler says so (Example 3). With enums and sealed types, which come later, you can be exhaustive without a default by listing all the possibilities; a later module uses this.
This requirement is a safety feature: you can no longer forget the "else" case, and the variable always gets a value.
Several statements: blocks and yield
Usually a rule is one expression. When a case needs more steps, use a block and end it with yield, which hands the value to the expression:
String plan = switch (day) {
case 1, 2, 3, 4, 5 -> {
int hours = 8;
yield "Work " + hours + "h";
}
case 6 -> "Half day";
default -> "Off";
};yield is like return for the switch: it ends the block and supplies the value. Keep blocks short; if one grows large, the logic probably belongs somewhere else.
The arrow form as a statement
You can use the arrow form without producing a value, as in the first snippet. It is a statement and does not have to be exhaustive. It is the modern replacement for the classic switch in many codebases because there is no break to forget.
Version note
Switch expressions were previewed in JDK 12 and 13 and became a standard feature in Java 14 (JEP 361). Any JDK from version 14 onward supports them, including the one used for this course's examples (JDK 21). If you ever work in a project that still compiles with Java 8 or 11, only the classic form exists there; check the project's Java version before using the arrow form.
Examples
Example 1: weekend or weekday as a value (simple)
int day = 6;
String type = switch (day) {
case 6, 7 -> "Weekend";
default -> "Weekday";
};
System.out.println(type);Output: Weekend. Compare with Lesson 3's version: no break, no stacked labels, no variable declared ahead of time and assigned in several places.
Example 2: days in a month and a command word (practical)
int m = 2;
int days = switch (m) {
case 4, 6, 9, 11 -> 30;
case 2 -> 28;
default -> 31;
};
System.out.println("Month " + m + ": " + days);Output: Month 2: 28. The classic version took twelve lines; this takes five and cannot fall through. As in Lesson 3, leap years are ignored here.
With a String selector and a block:
int day = 6;
String plan = switch (day) {
case 1, 2, 3, 4, 5 -> {
int hours = 8;
yield "Work " + hours + "h";
}
case 6 -> "Half day";
default -> "Off";
};
System.out.println(plan);Output: Half day.
Example 3: forgetting default, and the arrow as a statement (realistic)
A developer writes a switch expression that handles only weekend days:
int day = 3;
String t = switch (day) {
case 6, 7 -> "Weekend";
};The compiler stops it (real message from JDK 21):
L04b.java:4: error: the switch expression does not cover all possible input values
String t = switch (day) {
^Adding default -> "Weekday"; fixes it. The compiler has just saved you from a program that would have had no answer for 3.
Duplicate labels are still caught:
L04d.java:13: error: duplicate case label
case 1 -> "b";
^Finally, the earlier menu example from Lesson 3 as an arrow statement, where a missing break is impossible:
int option = 2;
switch (option) {
case 1 -> System.out.println("Open");
case 2 -> System.out.println("Save");
case 3 -> System.out.println("Exit");
default -> System.out.println("Unknown");
}Output: Save only. Compare with the output Save followed by Exit that the classic version without break produced.
Backend Insight
Services often translate one code into another: an order status into a label for the customer, an HTTP status into a category, a plan name into a limit. A switch expression expresses "this value maps to that value" in one readable block, and its exhaustiveness check turns forgotten cases into compile errors instead of production bugs.
Later versions of Java and the sealed types you will meet in the Junior level extend this: when a switch expression covers every variant of a type, the compiler tells you when someone adds a new variant and the switch is no longer complete. That is a powerful way to keep business rules in sync with the data they handle, and it starts here with a plain default.
One practical note: a default in a switch expression that silently returns a harmless value can hide bad input. For values that should never occur, a later module on exceptions shows how to fail loudly instead.
Common Mistakes
Mistake 1: Mixing arrows and colons
- Incorrect:
case 1 -> ...andcase 2:in the same switch. - Correct: pick one style per switch.
Real message:
L04e.java:6: error: different case kinds used in the switch
case 2: System.out.println("b");
^Mistake 2: Missing the semicolon after a switch expression
- Incorrect: closing the braces without
;when the switch is on the right of=. - Correct:
};
Real message:
L04c.java:7: error: ';' expected
}
^Mistake 3: No default in an expression over int or String
See Example 3. Add default. The compiler error is a feature, not an obstacle.
Mistake 4: Putting break in an arrow rule out of habit
In the arrow form a break is not needed, and you cannot use it to leave a switch expression. If a case needs several statements, use a block and yield.
Mistake 5: Types that do not match the assignment
int days = switch (m) { case 2 -> "short"; default -> 31; }; mixes a String and an int, so there is no single type for the value. Make every rule produce the same kind of value.
Exercises
Level A: Foundation
A1. What are two things the arrow form does better than the classic form?
أظهر الحل A1
A1. No fall-through, so no break to forget; and several constants can share a rule with a comma. A switch expression can also produce a value directly.
A2. What does this print?
int n = 7;
String s = switch (n) {
case 1, 3, 5, 7, 9 -> "odd digit";
case 0, 2, 4, 6, 8 -> "even digit";
default -> "not a digit";
};
System.out.println(s);أظهر الحل A2
A2. odd digit, because 7 is listed in the first rule.
A3. Why does a switch expression over an int need a default?
أظهر الحل A3
A3. An expression must always produce a value, and an int has far too many possible values to list, so default covers the rest. The compiler rejects the switch otherwise.
Level B: Application
B1. Rewrite this classic switch as a switch expression assigned to String name:
switch (n) {
case 1: name = "one"; break;
case 2: name = "two"; break;
case 3: name = "three"; break;
default: name = "many";
}أظهر الحل B1
B1.
String name = switch (n) {
case 1 -> "one";
case 2 -> "two";
case 3 -> "three";
default -> "many";
};B2. Write a switch expression that gives a ticket price from a String category: "child" costs 5, "adult" costs 10, "senior" costs 7, anything else costs 10.
أظهر الحل B2
B2.
int price = switch (category) {
case "child" -> 5;
case "adult" -> 10;
case "senior" -> 7;
default -> 10;
};Letter case matters: "Child" would fall to default.
B3. The code below does not compile. Say why and fix it in two different ways.
int level = 2;
String access = switch (level) {
case 1 -> "read";
case 2 -> "write";
case 3 -> "admin";
};أظهر الحل B3
B3. The expression is not exhaustive: an int can be 4, 0, or anything else. Fix 1: add default -> "none";. Fix 2: replace the case for 3 by default -> "admin"; if level 3 is the only remaining possibility you care about (less clear, since other values would also get admin). Fix 1 is better.
Level C: Challenge
C1. Write a switch expression that maps a month number to a quarter string Q1 to Q4 using grouped labels, with default returning invalid. List the test values you would use and the expected results.
أظهر الحل C1
C1.
String quarter = switch (month) {
case 1, 2, 3 -> "Q1";
case 4, 5, 6 -> "Q2";
case 7, 8, 9 -> "Q3";
case 10, 11, 12 -> "Q4";
default -> "invalid";
};Tests: 1 and 3 give Q1, 4 gives Q2, 9 gives Q3, 10 and 12 give Q4, 0 and 13 give invalid.
C2. Given char op and two int values a and b, write a switch expression that computes +, -, *, and / and returns 0 for anything else. Explain what happens for '/' with b = 0 and what you would want to happen in a real program.
أظهر الحل C2
C2.
int result = switch (op) {
case '+' -> a + b;
case '-' -> a - b;
case '*' -> a * b;
case '/' -> a / b;
default -> 0;
};With integer division, a / 0 makes the program stop with an ArithmeticException at run time. In a real program you would check b != 0 first with an if and report an error, because returning 0 for a bad operator or a division by zero hides a problem. Exceptions are covered in a later module.
Mini Challenge
In main, write a plan pricing calculation using a switch expression: from a String plan ("free", "basic", "pro") produce a monthly price (0, 9, 29) and, in a second switch expression, a user limit (1, 5, 50). Anything else should give a clearly marked fallback value (such as -1) so that bad input is visible.
Check your work: a typo like "Pro" produces the fallback, you wrote the expected values before running, and neither switch has a break.
Summary & Next Lesson
Key takeaways
- The arrow form
case X -> ...runs only its own rule: no fall-through and nobreak. - A switch expression produces a value and ends with a semicolon when assigned.
- Commas let several constants share one rule.
- A switch expression must cover every case; for
intandStringthat meansdefault. - Use a block with
yieldwhen a rule needs several statements. - It needs Java 14 or later.
Next lesson
You can now pick the right tool for each decision. The next lesson, "Taming nested conditions", deals with decisions inside decisions: how to flatten deep if nesting using &&, ||, early exits, and named conditions.
Self-check
- What replaces
breakin the arrow form? - What must a switch expression do that a switch statement need not?
- How do you give two constants the same rule in the arrow form?
- When do you need
yield? - Which Java version made switch expressions a standard feature?
أظهر الإجابات
- Nothing: each arrow rule runs alone, so no
breakis needed. - Cover every possible input value and always produce a value.
- Separate them with commas:
case 6, 7 -> .... - When a rule is a block with several statements and must hand back a value.
- Java 14.
References
- OpenJDK, JEP 361: Switch Expressions. Confirms that switch expressions became a standard feature in Java 14 after previews in JDK 12 and 13.
- Oracle, The Java Language Specification, Java SE 21, Chapter 14. Supports the statement that arrow rules do not fall through while old-style labels do.
- The exhaustiveness rule,
yield, and the compiler messages were verified by compiling and running the examples with JDK 21; I did not find a separate page to cite for exhaustiveness.
التطبيق
طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.
تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.