From Java to Kotlin – Part V: Switch vs When
Considering a move to Kotlin? Coming from a Java background? In this short series of blog posts, I’ll take a look at familiar, straightforward Java concepts and demonstrate how you can approach them in Kotlin. While many of these points have already been discussed in earlier posts by colleagues, my focus is simple: how you used to do it in Java, and how you do it in Kotlin.
Case 5:
Java has switch.
Kotlin has when.
They look similar.
They are not.
The problem
We want to map values to readable output. Strings, numbers, ranges, types.
The biggest problem: My colleague Jacob 👋 pointed out my comparison was not fair (and he was right!)…
The Java way (modern)
Since Java 14 (finalised in Java 17), switch is no longer just a classic control structure.
It became an expression that can return values directly.
With Java 21, pattern matching for switch was finalised as well, making it type-aware and allowing guarded cases.
private String getColorName(Object color) {
return switch (color) {
case String s when s.equals("R") -> "Red";
case String s when s.equals("G") -> "Green";
case String s when s.equals("B") -> "Blue";
case String s when s.equals("Y") || s.equals("O") -> "Yellow or Orange";
case Number _ -> "Number";
case null, default -> "Unknown";
};
}
Or, if you prefer to keep the string mapping separate:
private static String getColorName(Object color) {
return switch (color) {
case String s -> switch (s) {
case "R" -> "Red";
case "G" -> "Green";
case "B" -> "Blue";
case "Y", "O" -> "Yellow or Orange";
default -> "Unknown";
};
case Number _ -> "Number";
case null, default -> "Unknown";
};
}
Usage:
getColorName("G"); // "Green"
getColorName(5); // "Number"
Clean. Type-aware. An expression.
Switch expressions were introduced in JEP 361: Switch Expressions.
Pattern matching for switch went through several preview iterations
(JEP 406, 420, 427, 433) and was finalised in
JEP 441: Pattern Matching for switch.
The Kotlin way
Kotlin’s when has offered this expressive style from day one:
private fun getColorName(color: Any = "B"): String {
return when (color) {
"R" -> "Red"
"G" -> "Green"
"B" -> "Blue"
in setOf("Y", "O") -> "Yellow or Orange"
is Number -> "Number"
else -> "Unknown"
}
}
Usage:
getColorName("G") // Returns "Green"
getColorName(5) // Returns "Number"
getColorName("Y") // Returns "Yellow or Orange"
getColorName("O") // Returns "Yellow or Orange"
And since Kotlin 2.1, guard conditions are supported as well:
Before:
when (input) {
is String -> when {
input.length == 1 -> "Single character"
input.all { it.isDigit() } -> "Numeric string"
else -> "Regular string"
}
}
With guard conditions:
fun describe(input: Any): String =
when (input) {
is String if input.length == 1 -> "Single character"
is String if input.all { it.isDigit() } -> "Numeric string"
is String -> "Regular string"
is Int if input > 0 -> "Positive number"
is Int -> "Non-positive number"
else -> "Unknown"
}
So what’s the difference today?
Both:
-
are expressions
-
support type checks
-
return values directly
-
avoid fall-through
-
improve readability compared to classic
switch
In other words: they have clearly converged.
The real differences are now more subtle and mostly stylistic:
Java switch:
-
strongly tied to pattern matching for
instanceof -
more explicit and verbose
-
designed to fit into Java’s gradual evolution
Kotlin when:
-
slightly more concise
-
supports ranges and collections more naturally
-
allows smart casts without extra ceremony
Takeaway
This is no longer a story of:
Java → control structure Kotlin → expression
Both languages now ask the same question:
“What is this value?”
The gap that once existed between switch and when has become surprisingly small.