La expresión Switch en Java ha recorrido un largo camino. Comenzó como una función de vista previa en Java 12 y recibió mejoras constantes en cada nueva versión de Java. Finalmente estuvo disponible en la versión Java 17 (LTS), sin embargo, en Java 21 (LTS) ahora tiene funciones más nuevas y cambios con respecto a las funciones de Java 17.
Este artículo se basa en uno anterior Expresión Switch en Java 17, pero destaca los cambios que ahora están disponibles en Java 21. Comencemos…
Pattern Matching por Tipo de Dato
Este es el primer paso hacia un estilo de programación más declarativo y “null-safe”, permitiendo una mejor manera de expresar y modelar datos, reconociendo la semántica del modelo de datos por medio de patrones. Mejorando de forma definitiva la legibilidad de nuestro código, evitando if anidados para muchas circunstancia, y proporcionando características que vemos en otros lenguajes de programación.
Tomemos como referencia el siguiente código, con varios if/else if
e instanceof
.
static String formatter(Object o) { String formatted = "unknown"; if (o instanceof Integer i) { formatted = String.format("int %d", i); } else if (o instanceof Long l) { formatted = String.format("long %d", l); } else if (o instanceof Double d) { formatted = String.format("double %f", d); } else if (o instanceof String s) { formatted = String.format("String %s", s); } return formatted; }
El código anterior se puede transformar con una expresión switch de la siguiente manera
static String formatterPatternSwitch(Object o) { return switch (o) { case Integer i -> String.format("int %d", i); case Long l -> String.format("long %d", l); case Double d -> String.format("double %f", d); case String s -> String.format("String %s", s); default -> o.toString(); }; }
Switch Expression vs Switch Statement
Ahora bien, ¿Cómo van a co-existir el “switch expression” y el “switch statement”? La primera diferencia está en el operador utilizado luego del case
, la expresión utiliza una flecha (arrow) ->
mientras que el “statement” continua usando el operador :
(dos puntos).
//Switch Expression example switch (o) { case Integer i -> String.format("int %d", i); case Long l -> String.format("long %d", l); } //Switch Statement example switch (o) { case Integer i: String.format("int %d", i); break; case Long l: String.format("long %d", l); break; }
Se puede notar que en los “switch statement” aún es necesario utilizar la sentencia break
para terminar el flujo del switch, es decir que si no se indica break
seguirá ejecutando los bloques de código de los siguientes labels. Por otro lado, en la expresión switch, solamente se ejecuta bloque de código del patrón o valor con el que hizo “match”.
Cabe destacar, que tanto la sentencia como la expresión siguen funcionando con valores exactos o constantes, no solamente con patrones a los que hace match. Por ejemplo, el siguiente código sigue siendo funcional.
//Switch statement switch (value) { case "A": callMethod1(); break; case "B": callMethod2(); break; default: callMethod3(); } //Switch Expression switch (value) { case "A"-> callMethod1(); case "B"-> callMethod2(); default -> callMethod3(); }
Soporte del null
Tradicionalmente el switch lanza un NullPointerException
cuando el valor el nulo. Este comportamiento sigue presente para los switch statements existentes, que no hacen uso de patrones. Sin embargo, para hacer más amigable las nuevas características del switch (tanto el statement como la expresión) permite null
como parte del patrón.
switch (o) { case null -> System.out.println("null!"); case String s -> System.out.println("String"); default -> System.out.println("Something else"); }
Guarded Pattern
Este es una característica que he visto en otros lenguajes y me parece muy acertado que se haya agregado en Java también. Un “guarded pattern” es una condición que forma parte de expresión que debe hacer match y es precedida del operador when
Este es uno de los cambios con respecto a Java 17, donde el operador para el patrón protegido era &&
. Sin embargo, se modificó para evitar confundir a los desarrolladores con otros usos de &&
.
switch(value) { case String s when (s.length > 3) -> System.out.println("A short string"); case String s when (s.length > 10) -> System.out.println("A medium string"); default -> System.out.println("A long string"); }
El Guarded Pattern también se puede aplicar a declaraciones Switch, no solo a expresiones Switch.
Retornando valores en bloques de código
Switch Expressions permite devolver valores, si después del operador flecha ->
el bloque de código es solo una línea, no es necesario usar ninguna “palabra clave”. Devolverá el resultado de esa línea de código por defecto.
enum WeekDay {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY;} String mood = switch (days) { case SUNDAY -> "Resting"; case MONDAY -> "Boring"; case TUESDAY -> "Go ahead"; case WEDNESDAY -> "Hump day"; case THURSDAY -> "Weekend is comming"; case FRIDAY -> "Weekend!!"; case SATURDAY -> "Yeah"; }
Como puede ver, el valor se devuelve automáticamente, pero si tiene un bloque de código con varias líneas para cualquiera de esos casos, se requiere una palabra clave al final. En Java 17, esa clave era return
, sin embargo, confundía a los desarrolladores con el return
del método, por lo tanto, para Java 21 esa palabra clave ahora es yield
.
enum WeekDay {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY;} String mood = switch (days) { case SUNDAY -> "Resting"; case MONDAY -> "Boring"; case TUESDAY -> "Go ahead"; case WEDNESDAY -> "Hump day"; case THURSDAY -> "Weekend is comming"; case FRIDAY -> { System.out.println("Need to do pending stuff"); yield "Weekend"; }; case SATURDAY -> "Yeah"; }
Eche un vistazo a FRIDAY
, tiene un bloque de código con más de una línea, por lo tanto, cuando devuelve su valor, utiliza la palabra clave yield
.
Múltiples etiquetas por case
Otra característica interesante para mejorar la legibilidad del código es la posibilidad de tener etiquetas de casos con múltiples opciones.
enum WeekDay {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY;} boolean gymDay = switch (days) { case MONDAY, TUESDAY, THURSDAY, FRIDAY -> true; default -> false; }
En lugar de tener múltiples etiquetas de caso con el mismo resultado, es posible tener un caso con múltiples etiquetas.
Conclusión
Hay muchas otras características del switch que no son enumeradas en este artículo, sin embargo, se trata de mostrar las principales, que considero pueden proporcionar una gran mejora en usabilidad a nuestro código.
Definitivamente, Java como lenguaje sigue evolucionando y proporcionando características que simplificarán más nuestro código y haciendo que se mantenga a la par de muchos otros lenguajes nuevos que suelen son tendencia en este momento.
Referencias
- https://openjdk.java.net/jeps/406
- https://www.infoworld.com/article/3606833/jdk-17-the-new-features-in-java-17.html
- https://docs.oracle.com/en/java/javase/21/language/pattern-matching-switch.html#GUID-E69EEA63-E204-41B4-AA7F-D58B26A3B232
- https://belief-driven-design.com/looking-at-java-21-switch-pattern-matching-14648/
- https://blog.adamgamboa.dev/switch-expression-on-java-17/