From printTriangularNumber to Duff’s Device: Mastering Java Switch Statements Old and New
This post traces that journey using triangular number computation as a practical example of intentional fall-through and connects the technique to Duff's Device.
Join the DZone community and get the full member experience.
Join For FreeIn this blog post, we will see how the humble Java switch statement evolved from a fall-through curiosity into a powerful expression, and how understanding its mechanics unlocks classic techniques like Duff's Device.
Java's switch statement has evolved from a fall-through-prone construct into a modern expression syntax introduced in Java 14. The post traces this evolution using a concrete example, a method that computes triangular numbers by intentionally allowing execution to cascade through cases without break statements.
The post also connects this behavior to Duff's Device, a 1983 loop-unrolling technique that uses deliberate fall-through to handle remainder elements before processing full blocks. A comparison of old and new switch syntax outlines trade-offs, and practical guidance is offered on when each form is appropriate.
The Accidental Discovery
I was prepping for the OCP Java 21 exam and stumbled across a tricky question. A method named question2 used a switch statement without any break statements. The output surprised me at first.
Once I traced through it, I renamed the method to printTriangularNumber. That one rename told the whole story. This post dives into why.
The Old Switch Statement
The traditional switch statement has been part of Java since day one. The syntax looks like this:
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("Unknown");
break;
}
As shown above, every case ends with a break. Without it, execution does not stop. It keeps going into the next case.
The old switch works on int, char, String, and enum types.
Fall-Through: Feature or Bug?
The most misunderstood behavior in switch is fall-through. When you omit break, execution literally falls into the next case.
int x = 2;
switch (x) {
case 3: System.out.println("three");
case 2: System.out.println("two"); // jumps here
case 1: System.out.println("one"); // falls through
default: System.out.println("done"); // falls through
}
Output:
two
one
done
Most developers treat this as a bug waiting to happen. They are not wrong. Forgetting a break is one of the most common Java mistakes. But intentional fall-through is a different story. It is a deliberate tool. And printTriangularNumber is the perfect example.
printTriangularNumber: Fall-Through in Action
Here is the method I renamed from question2 during my OCP prep:
private static void printTriangularNumber(int n) {
int res = 0;
switch (n) {
case 5:
res += 5;
case 4:
res += 4;
case 3:
res += 3;
case 2:
res += 2;
case 1:
res += 1;
default:
break;
}
System.out.println(res == 0 ? "Ok, bye." : res);
Let us trace through n = 4:
- Jumps to
case 4, adds 4.res = 4 - Falls to
case 3, adds 3.res = 7 - Falls to
case 2, adds 2.res = 9 - Falls to
case 1, adds 1.res = 10 - Hits
default, breaks
Output: 10
The pattern for each input:
| n | Result | Formula |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 3 | 2+1 |
| 3 | 6 | 3+2+1 |
| 4 | 10 | 4+3+2+1 |
| 5 | 15 | 5+4+3+2+1 |
This is n * (n + 1) / 2, the triangular number formula. The fall-through is doing the summation for you. Each case accumulates the remaining values by simply not stopping.
For n = 0 or any value above 5, no case matches, default fires immediately, and res stays 0. The ternary prints "Ok, bye.".
I personally find it a beautiful example of using language semantics intentionally. This is also the kind of question the OCP exam loves to throw at you.
The New Switch Expression (Java 14+)
Java 14 introduced switch expressions as a standard feature. The arrow syntax -> eliminates fall-through entirely. Each arm is independent.
int day = 3;
String name = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
default -> "Unknown";
};
System.out.println(name); // Wednesday
A few things to notice here:
- Switch is now an expression. It returns a value.
- The arrow
->replaces:andbreaktogether. - No fall-through. Each arm executes independently.
- Multiple labels on a single arm:
case 1, 7 -> "Weekend";
You can also use it inline:
System.out.println(switch (day) {
case 1, 7 -> "Weekend";
default -> "Weekday";
});
Much cleaner. Much safer.
Switch Expressions With Yield
Sometimes you need more than a single expression in an arm. That is where yield comes in.
int n = 4;
int result = switch (n) {
case 1, 2 -> n * 10;
case 3, 4 -> {
int temp = n * n;
System.out.println("Computing for: " + n);
yield temp; // return value from block
}
default -> 0;
};
System.out.println(result); // 16
Think of yield as the return statement for a switch block arm. You need it whenever the arm has multiple statements inside {}.
A common mistake is using return instead of yield inside a switch expression block. That compiles only inside a method and it returns from the entire method, not just the switch. Always use yield inside switch expression blocks.
Duff's Device: Fall-Through Taken to the Extreme
Now that we understand fall-through well, let us look at the most famous intentional use of it: Duff's Device.
Tom Duff invented this in 1983 to speed up memory copy operations by reducing loop branch overhead. The trick is to unroll the copy loop and use a switch to jump into the middle of it based on the remainder.
In Java, we replicate it in two clean phases since Java does not allow interleaved switch+loop syntax:
public static void duffCopy(int[] src, int[] dst, int n) {
int i = 0;
int rem = n % 4;
// Phase 1: handle remainder via fall-through
switch (rem) {
case 3: dst[i] = src[i]; i++;
case 2: dst[i] = src[i]; i++;
case 1: dst[i] = src[i]; i++;
case 0: break;
}
// Phase 2: full blocks of 4
int fullBlocks = (n - rem) / 4;
while (fullBlocks-- > 0) {
dst[i] = src[i]; i++;
dst[i] = src[i]; i++;
dst[i] = src[i]; i++;
dst[i] = src[i]; i++;
}
}
Let us trace through n = 13:
rem = 13 % 4 = 1- Switch jumps to
case 1, copies 1 element.i = 1 fullBlocks = (13 - 1) / 4 = 3- Loop runs 3 times, copying 4 elements each time
- Total: 1 + 12 = 13 elements
The Python equivalent makes the two phases explicit:
def duff_copy(src, n):
dst = [None] * n
rem = n % 4
for i in range(rem): # Phase 1: remainder
dst[i] = src[i]
i = rem
while i < n: # Phase 2: full blocks
dst[i] = src[i]
dst[i+1] = src[i+1]
dst[i+2] = src[i+2]
dst[i+3] = src[i+3]
i += 4
return dst
The connection to printTriangularNumber is direct. Both use fall-through intentionally. In printTriangularNumber, the switch jumps to the right case and accumulates downward. In Duff's Device, the switch jumps to the right case and copies the remainder before the main loop takes over.
Old vs. New Switch at a Glance
| Feature | Old Switch (:) |
New Switch (->) |
|---|---|---|
| Fall-through | Yes (default) | No |
| Returns value | No | Yes |
break needed |
Yes | No |
| Multiple labels | No | Yes (case 1, 2 ->) |
| Block with yield | No | Yes |
| Null safe | No | Yes (Java 21 preview) |
| OCP exam topic | Yes | Yes |
Which One Should You Use?
For new code, always prefer the switch expression with ->. It is safer, cleaner, and expressive. Your reviewers will thank you.
Reserve the old switch with fall-through only when you genuinely need the cascading behavior, like in printTriangularNumber or a hand-tuned loop like Duff's Device. In those cases, add a comment explaining the intent. Otherwise, the next developer (including future you) will assume the break is missing by accident.
My personal observation: the OCP Java 21 exam tests both heavily. Knowing when fall-through is intentional versus accidental is the key distinction examiners probe. Make sure you can trace through any switch block without running it.
Happy testing!
What is your take: is intentional fall-through clever engineering or a maintenance nightmare waiting to happen? Drop your thoughts below!
Published at DZone with permission of NaveenKumar Namachivayam. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments