Expresiones switch modernas en Java
Usa expresiones switch modernas en Java con etiquetas de flecha, yield, exhaustividad y coincidencia de patrones.
La instrucción switch tradicional ha formado parte de Java desde la versión 1.0, pero traía mucho equipaje: errores por caída entre casos, instrucciones break repetitivas y ninguna forma de producir un valor. Las expresiones switch, finalizadas en Java 14, solucionan todo eso. Convierten switch de una torpe instrucción de flujo de control en una expresión concisa que produce valores.
Este capítulo recorre el switch moderno: etiquetas de flecha, la palabra clave yield, casos con múltiples etiquetas, comprobación de exhaustividad y exactamente en qué se diferencia la nueva forma de la instrucción que ya puedes conocer.
De instrucción a expresión
La instrucción switch clásica ejecuta efectos secundarios y depende de break para detener la caída. Si olvidas un break, la ejecución cae silenciosamente en el siguiente caso, lo que es una fuente notoria de errores.
// Traditional switch statement (error-prone)
String kind;
switch (day) {
case SATURDAY:
case SUNDAY:
kind = "weekend";
break; // forget this and you fall through
default:
kind = "weekday";
}Una expresión switch reduce eso a una sola asignación. La forma de flecha (->) nunca produce caída entre casos, por lo que no se necesita break.
// Modern switch expression
String kind = switch (day) {
case SATURDAY, SUNDAY -> "weekend";
default -> "weekday";
};Todo el switch ahora evalúa a un valor, que puedes asignar, devolver o pasar como argumento directamente.
Etiquetas de flecha y casos con múltiples etiquetas
La etiqueta de flecha case L -> asocia una etiqueta (o varias, separadas por comas) con una única acción. Solo se ejecuta la rama coincidente; no hay caída entre casos de la que preocuparse.
int numLetters = switch (month) {
case JANUARY, JUNE, JULY -> 4;
case FEBRUARY, MARCH, APRIL, MAY -> 5;
case SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> switchOnLength(month);
case AUGUST -> 6;
};Agrupar etiquetas con comas reemplaza el antiguo truco de apilar líneas case vacías para compartir un cuerpo, y resulta mucho más legible.
| Característica | switch tradicional (case L:) | switch moderno (case L ->) |
|---|---|---|
| Caída entre casos | Sí, a menos que uses break | No, cada rama está aislada |
| Produce un valor | No | Sí (es una expresión) |
| Múltiples etiquetas | Líneas case vacías apiladas | Separadas por comas en una línea |
| Ámbito de variables | Compartido en todo el bloque | Local a cada bloque de rama |
Bloques y la palabra clave yield
Cuando una rama necesita más que una sola expresión, usa un bloque { ... } y devuelve su valor con yield. La palabra clave yield es para una expresión switch lo que return es para un método: proporciona el valor que produce la rama.
int gradePoints = switch (grade) {
case 'A' -> 4;
case 'B' -> 3;
default -> {
log("Unknown grade: " + grade);
yield 0; // the value this branch evaluates to
}
};Aún puedes usar etiquetas de dos puntos con yield si prefieres la sintaxis antigua, pero la forma de flecha es la elección moderna idiomática y evita completamente la caída accidental.
Exhaustividad y default
Una expresión switch debe ser exhaustiva: toda entrada posible debe manejarse, porque la expresión tiene que producir un valor sin importar qué. Para la mayoría de los tipos, esto se satisface con una rama default. Para un enum, el compilador puede verificar la exhaustividad directamente; si cubres todas las constantes, default se vuelve opcional.
// No default needed: all enum constants are covered,
// so the compiler knows the switch is exhaustive.
boolean isWeekend = switch (day) {
case SATURDAY, SUNDAY -> true;
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> false;
};Si omites una constante y no hay default, el código no compilará. Esta red de seguridad en tiempo de compilación es uno de los mayores beneficios prácticos sobre la instrucción antigua, que silenciosamente dejaría una variable sin asignar.
Coincidencia de patrones en switch
Las versiones recientes de Java extienden switch para que cada case pueda coincidir con el tipo del valor, no solo con una constante. Esto es la coincidencia de patrones para switch (en vista previa en Java 17–20, finalizada en Java 21). En lugar de escribir una cadena de comprobaciones instanceof y conversiones de tipo, etiquetas cada rama con un patrón de tipo y enlazas una variable en el mismo paso.
static String describe(Object obj) {
return switch (obj) {
case Integer i -> "int " + i; // matches and binds i
case String s -> "string of length " + s.length();
case null -> "nothing"; // null can be its own label
default -> "something else";
};
}La coincidencia de patrones es especialmente poderosa con records y tipos sellados: cuando un switch cubre cada subtipo permitido de un tipo sellado, el compilador lo trata como exhaustivo, por lo que puedes eliminar default por completo.
sealed interface Shape permits Circle, Square {}
record Circle(double radius) implements Shape {}
record Square(double side) implements Shape {}
static double area(Shape shape) {
return switch (shape) { // no default: all permitted types covered
case Circle c -> Math.PI * c.radius() * c.radius();
case Square s -> s.side() * s.side();
};
}switch solo podía probar un enum, un tipo integral o un String. Los patrones de tipo permiten que switch funcione con cualquier tipo de referencia, lo que lo convierte en un reemplazo natural para largas escaleras if/else if.Un ejemplo completo trabajado
El programa siguiente une todas las piezas: etiquetas de flecha con casos de múltiples etiquetas, un bloque yield para una rama calculada, cobertura exhaustiva de enums y una expresión switch asignada directamente a una variable.
Lo que se puede extraer de la ejecución:
kind()devuelve el resultado de una expresión switch directamente;MONDAYyFRIDAYimprimenweekday,SATURDAYimprimeweekend, todo sin un solobreak.- Las dos ramas de flecha en
kind()cubren las siete constantes del enum, por lo que el switch es exhaustivo y no necesitadefault. - En
letterGrade(), las puntuaciones 95, 83, 71 y 64 se asignan limpiamente a través de etiquetas de flecha a 4, 3, 2 y 1 punto de calificación. - La puntuación 42 llega al bloque
default, que primero imprime(failing score 42)mediante la línea de efecto secundario y luego haceyieldde 0, mostrando cómo una rama de bloque puede hacer trabajo antes de producir su valor. - El
switchfinal asignaTWOdirectamente a la variablelabel, lo que demuestra que una expresión switch es un valor que puedes almacenar, no solo flujo de control.
Cuándo usar cada forma
- Usa una expresión switch (
case L ->) siempre que el objetivo sea calcular y devolver un único valor. Es exhaustiva, libre de caídas y se lee como una sola asignación. - Una instrucción switch tradicional sigue teniendo sentido cuando cada rama es puramente un efecto secundario (registro, despacho) y no hay ningún valor que producir.
- Usa patrones de tipo cuando estés ramificando según el tipo en tiempo de ejecución de un objeto, especialmente entre las constantes de un
enumo los subtipos de un tipo sellado.
Para la historia completa y la forma con etiquetas de dos puntos, consulta expresiones switch de Java.