3. Selección: switch expression
UD3 — Control de Flujo y Depuración · RA3 — Escribe y depura código, analizando y utilizando las estructuras de control del lenguaje
IES Thiar · Curso 2026 - 2027
| Aspecto | Valor |
|---|---|
| Resultado de aprendizaje | RA: 3 — Escribe y depura código, analizando y utilizando las estructuras de control del lenguaje. |
| Criterios de evaluación a los que contribuye esta sección | CE 3.1 (escribe y prueba código que hace uso de estructuras de selección) CE 3.5 (crea programas ejecutables combinando selección con lectura de datos) CE 3.7 (comenta y documenta el código). |
| Marco normativo | RD 405/2023 · RD 450/2010 · RD 659/2023 |
| Tecnología base | Java 25 LTS (OpenJDK Temurin 25) — soporte hasta 2030+ |
| Horas estimadas | ~5 h (de las 26 h de la UD3). |
Requisitos previos (lo que ya sabes): condiciones booleanas, operadores relacionales y lógicos, paréntesis, De Morgan y variables booleanas (sección 1 de UD3); sintaxis completa de
if/else if/else, early return, trampas delify validación conScanner(sección 2 de UD3); tipos primitivos,var,String(con.equals(),.equalsIgnoreCase(),.length()y.matches()),text blocks,finalpara constantes, operador ternario yInteger.parseInt/Double.parseDouble(de UD1); introducción básica al concepto deenumcomo conjunto de constantes nombradas (de UD1, sección 6).Lo que NO verás aquí (se reserva para secciones posteriores): bucles
while/do-while/for(secciones 4–6),break/continuecomo sentencias de salto de bucle (sección 7),try-catch(sección 8), excepciones propias (sección 9), depurador de IntelliJ (sección 10),assert(sección 11), pattern matching coninstanceofcompleto (sección 12). Tampoco verás la definición completa deenum(propiedades, constructores, métodos) ni la definición desealed classes: ambas se cubren en UD4; aquí las usaremos como "cajas negras" reconocibles. Tampoco aparece nada de POO (UD2) ni de colecciones (UD6): seguimos trabajando conmainy variables locales.
3.1 ¿De qué va esta sección? — Cuando el if se queda corto
En la sección 2 aprendiste a escribir if / else if / else y a validar entradas. Esa es la herramienta de selección más universal y la que más vas a usar. Pero hay un patrón que, escrito con if, queda verboso y repetitivo: cuando comparas el mismo valor contra muchos literales.
Acuérdate del ejemplo de los días de la semana que vimos al final de la sección 2:
Funciona, sí. Pero hay siete else if comparando dia contra un número. Cada rama repite la palabra dia ==, las llaves y el IO.println. Si tuvieras 20 casos (por ejemplo, un menú con 20 opciones), el código sería un ladrillo ilegible.
Para este patrón existe una herramienta mucho más elegante: el switch. En Java 25, el switch moderno (la switch expression estabilizada en Java 14 y ampliada con pattern matching en Java 21) es la forma idiomática de elegir entre muchos valores. Es más conciso, más seguro (no hay fall-through accidental) y más expresivo (puede devolver un valor, como el operador ternario).
En esta sección vas a aprender a usarlo bien. Cubriremos, en este orden:
- El
switchclásico concase/break/default(legado, pero lo vas a encontrar en código antiguo). - El
switchexpression con flecha->, la forma moderna y recomendada. - El
switchcomo expresión (no sentencia):var r = switch(...) { ... };. - Múltiples etiquetas en un
case:case 1, 2, 3 -> .... switchconString.switchconenum(introducción; la definición completa de enums se ve en UD4).switchcon tipos (Java 21+): pattern matching en switch.- Guards con
when(Java 21+):case Integer i when i > 0 -> .... - Exhaustividad y por qué el compilador te obliga a poner
defaulto a cubrir todos los casos. - Cuándo
switchy cuándoif: criterios de decisión. - Trampas clásicas del switch.
- Ejemplos resueltos paso a paso.
Analogía del ascensor vs el portero
Imagina que quieres saber a qué planta va cada botón de un ascensor. Con if, sería como si un portero te preguntara: "¿Es el botón 1? No. ¿Es el 2? No. ¿Es el 3? Sí, pues al tercero". Con switch, es como un panel de botones: pulsas el 3 y la lógica salta directamente a esa planta, sin pasar por las demás. Mucho más rápido y más claro.
Vamos a empezar por el switch clásico, porque lo vas a encontrar en muchísimo código heredado (en proyectos open source, en libros antiguos, en tu futuro trabajo). Después pasaremos al moderno, que es el que escribirás tú.
3.2 El switch clásico con case / break / default
3.2.1 Sintaxis del switch clásico
El switch clásico (también llamado switch statement) lleva en Java desde la versión 1.0 (1996). Su sintaxis:
Partes:
selector: la variable o expresión cuyo valor se compara. Tiene que ser de tipobyte,short,int,char,Stringoenum. No admitelong,float,doubleniboolean.case valor:: cada etiqueta compara el selector contravalor. Si coincide, se ejecuta el bloque a partir de ahí.break;: al final de cada bloque, sale del switch. Si te lo olvidas, se ejecuta también el siguientecase(lo que se llama fall-through).default:: se ejecuta si ningúncasecoincide. Es opcional, pero casi siempre se pone.
3.2.2 Ejemplo: día de la semana (versión clásica)
Salida con dia = 3:
3.2.3 La trampa del fall-through
Aquí está la trampa más famosa del switch clásico. Mira este código sin break:
Si dia = 3, la salida es:
¿Qué ha pasado? Sin break, Java entra en el case 3 y sigue ejecutando todos los casos siguientes hasta encontrar un break o el final del switch. Esto se llama fall-through ("caer a través de").
A veces el fall-through se usa intencionadamente para agrupar casos:
Aquí los case 1 a case 5 están vacíos y "caen" hasta el bloque de "Día laborable". Funciona, pero es frágil: si un compañero añade un case 4 con su propio break sin darse cuenta de que forma parte de un grupo, se rompe la lógica.
Por eso, en Java moderno usamos la sintaxis con flecha (la verás en la sección 3.3), donde el fall-through no existe.
3.2.4 Restricciones del selector
El selector del switch clásico solo admite estos tipos:
| Tipo | Admite |
|---|---|
byte, short, int |
Sí |
char |
Sí |
String |
Sí (desde Java 7) |
enum |
Sí (desde Java 5) |
long, float, double |
No |
boolean |
No |
| Objetos arbitrarios | Solo con pattern matching (Java 21+, lo verás en sección 3.8) |
¿Por qué no boolean? Porque un boolean solo puede ser true o false, y para eso ya tienes if/else. Un switch para dos valores no aporta nada.
¿Por qué no long, float, double? Por decisiones históricas de diseño: el switch se diseñó para tipos "pequeños" (caben en un int), y los de coma flotante tienen problemas de precisión al comparar.
3.2.5 Diagrama de flujo del switch clásico
flowchart TD
Inicio([Inicio]) --> Eval["Evalúa el selector"]
Eval --> C1{"¿selector == valor1?"}
C1 -->|Sí| B1["Bloque 1 + break"]
C1 -->|No| C2{"¿selector == valor2?"}
C2 -->|Sí| B2["Bloque 2 + break"]
C2 -->|No| C3{"¿selector == valor3?"}
C3 -->|Sí| B3["Bloque 3 + break"]
C3 -->|No| Def["Bloque default"]
B1 --> Fin([Fin])
B2 --> Fin
B3 --> Fin
Def --> Fin
A diferencia del if/else if, en el switch clásico cada case es una etiqueta, no una condición. No hay que escribir selector == valor1, solo case valor1:. Java hace la comparación por ti.
3.2.6 Por qué lo seguimos viendo
Aunque en este curso escribiremos siempre la forma moderna (sección 3.3), el switch clásico sigue siendo código Java válido y lo vas a encontrar en:
- Libros antiguos y tutoriales de antes de 2020.
- Proyectos empresariales escritos en Java 7 u 8.
- Código generado automáticamente por herramientas como Lombok o IDEs.
- Stack Overflow y blogs de programación.
Por eso es importante que lo reconozcas al leerlo, aunque no lo escribas. Y, si un día lo heredas, sepas refactorizarlo a la forma moderna.
3.3 Switch expression con flecha (->)
3.3.1 La sintaxis moderna
Desde Java 14 (estabilizada), existe una nueva sintaxis de switch con flecha -> que soluciona todos los problemas del clásico:
Diferencias clave con el clásico:
| Característica | Clásico (:) |
Moderno (->) |
|---|---|---|
| Separador | case valor: |
case valor -> |
| Fall-through | Sí, si olvidas break. |
No existe: cada rama es exclusiva. |
break obligatorio |
Sí, para salir. | No, no se usa. |
Llaves { } |
Obligatorias siempre. | Solo si el bloque tiene varias sentencias. |
| Puede producir un valor | No (es sentencia). | Sí (es expresión). Lo verás en 3.4. |
3.3.2 Ejemplo: día de la semana (versión moderna)
Reescribamos el ejemplo de antes con la sintaxis moderna:
Fíjate:
- No hay
break. Cada rama es exclusiva: cuando se ejecutacase 3 -> IO.println("Miércoles");, se sale delswitchautomáticamente. - No hay llaves cuando el bloque es una sola sentencia. Si necesitas varias sentencias, las pones entre
{ }(lo verás en 3.3.4). default ->sinbreakni:.- El código es mucho más limpio que la versión clásica.
3.3.3 El fall-through ya no existe
En la sintaxis con flecha, no hay fall-through. Cada rama ejecuta una sola cosa y sale. Esto elimina de raíz el bug más común del switch clásico (olvidar el break).
Si quieres agrupar varios valores en una misma rama, usas múltiples etiquetas (sección 3.5), no fall-through:
Esto es inequívoco: los cinco primeros valores van a "Día laborable", y es imposible que un compañero rompa la lógica añadiendo un case 4 con su propio bloque.
3.3.4 Bloques con varias sentencias
Si una rama necesita varias sentencias, usas llaves:
Dentro del bloque { } puedes declarar variables, llamar a métodos, hacer lo que necesites. Es un mini-método en sí mismo.
3.3.5 Convenciones de estilo
IntelliJ IDEA te formatea el switch automáticamente con Ctrl+Alt+L. Las convenciones que seguimos en este curso:
- Una rama por línea. No apiles
case 1 -> ...; case 2 -> ...;en la misma línea. - Indentación consistente (4 espacios).
defaultsiempre al final, aunque Java no lo exija (lo verás en 3.10).- Si usas llaves, abre
{al final de la línea delcasey cierra}en una línea nueva, alineado concase.
3.4 Switch como expresión: devuelve un valor
3.4.1 La gran novedad: el switch que produce un valor
Hasta ahora, el switch era una sentencia: ejecutaba un bloque y ya. Pero en Java 14+, el switch también puede ser una expresión: produce un valor que puedes asignar a una variable, devolver con return o pasar como argumento. Esto cambia radicalmente cómo se escribe el código.
Sintaxis:
Detalles clave:
- La flecha
->ahora va seguida de una expresión (no de una sentencia), que es el valor que esa rama produce. - No hace falta
returndentro del switch: la expresión a la derecha de->es el valor. - El
;final es obligatorio: cierra la sentencia de asignación completa. - Si una rama necesita varias sentencias y devolver un valor, se usa
yield(lo verás en 3.4.4).
3.4.2 Ejemplo: nombre del día
Salida con dia = 3:
Esto es radicalmente más limpio que la versión con if/else if o con el switch clásico. Lo que antes eran 16 líneas (7 else if con sus llaves) ahora son 9.
3.4.3 Switch expression como argumento
Como el switch expression produce un valor, puedes usarlo dentro de otras expresiones:
El switch expression se evalúa, produce su valor, y ese valor se inserta en la expresión mayor. Igual que harías con el operador ternario, pero con muchos más casos.
3.4.4 yield para bloques multilínea
Si una rama necesita varias sentencias antes de devolver el valor, usas un bloque con llaves y la palabra reservada yield para indicar qué devuelve:
yield es el return del switch expression. Devuelve el valor de la rama actual y sale del switch.
Cuándo usar yield:
- Cuando una rama necesita calcular el valor en varios pasos.
- Cuando necesitas imprimir algo (un log) antes de devolver.
- Cuando el valor depende de comprobaciones adicionales dentro de la rama.
En la mayoría de los casos no necesitas yield: una sola expresión a la derecha de -> es suficiente. Úsalo solo cuando de verdad lo necesites.
3.4.5 Switch expression vs sentencia: ¿cuándo usar cuál?
| Caso | Recomendación |
|---|---|
| Quieres asignar/devolver un valor según una variable. | Switch expression (con =). |
| Quieres ejecutar acciones (imprimir, modificar estado) según una variable. | Switch statement moderno (con flecha, sin asignar). |
| La lógica de cada rama es compleja, varias sentencias. | Switch statement con bloques { }. |
| Necesitas un valor pero cada rama es muy simple. | Switch expression sin yield. |
Regla del profesor:
Si el switch produce algo (un valor), usa switch expression. Si el switch hace algo (efectos secundarios como imprimir), usa switch statement. En la práctica, el 80 % de los switches pueden ser expresiones.
3.4.6 La regla de la exhaustividad en switch expression
Una diferencia crítica: en un switch expression, TODOS los casos posibles deben estar cubiertos. No puedes olvidarte de default. Mira esto:
El compilador te obliga a cubrir todos los casos posibles. ¿Por qué? Porque el switch expression tiene que producir un valor siempre, así que si el selector toma un valor no contemplado, el programa no sabría qué devolver. Java se defiende exigiéndote un default:
Esta es una gran ventaja del switch expression: el compilador te obliga a pensar "qué pasa si el valor no es ninguno de los esperados". Con if/else if, es fácil olvidarse del else final y que el programa se calle en casos imprevistos.
Volveremos sobre esto en la sección 3.10.
3.5 Múltiples etiquetas en un case
3.5.1 Sintaxis de múltiples etiquetas
Una de las mejoras más útiles de la sintaxis moderna: puedes agrupar varios valores en un mismo case separándolos por comas:
Esto sustituye al fall-through del switch clásico y es mucho más legible.
3.5.2 Ejemplo: días laborables vs fin de semana
Salida con dia = 6:
Comparemos con la versión clásica con fall-through:
La versión moderna es claramente superior: una sola línea por grupo, sin break, sin riesgo de fall-through accidental.
3.5.3 Ejemplo: estación del año
Esto reemplaza con elegancia el if/else if que escribimos en la sección 2 para el mismo problema. Compara:
El switch es claramente más limpio: menos repetición de mes ==, menos llaves, menos líneas. Aquí es donde el switch brilla y el if se queda corto.
3.5.4 Múltiples etiquetas con bloques
Si una rama con múltiples etiquetas necesita varias sentencias:
Sin sorpresas: el bloque { } se ejecuta completo si el selector coincide con cualquiera de las etiquetas.
3.5.5 Restricciones con múltiples etiquetas
| Qué | Admitido |
|---|---|
Etiquetas separadas por comas en un case. |
Sí, con la flecha ->. |
Etiquetas separadas por comas en el switch clásico (:). |
Sí, desde Java 14 (pero se desaconseja el clásico). |
Rangos (case 1..5). |
No en Java (sí en Kotlin, Groovy, etc.). Usa múltiples etiquetas. |
Expresiones en etiquetas (case x+1). |
No. Solo literales y constantes final. |
La limitación de "no rangos" es importante: en Java no puedes escribir case 1..5 -> .... Tienes que escribir case 1, 2, 3, 4, 5 -> .... Es un poco más verboso, pero claro.
3.6 Switch con String
3.6.1 Sintaxis
Desde Java 7, el switch admite String como selector. Es perfectamente equivalente a comparar con .equals(), pero más conciso.
3.6.2 Switch con String es case-sensitive
El switch con String distingue mayúsculas y minúsculas, igual que .equals(). Si el usuario escribe "Admin" o "ADMIN", no entra en el case "admin".
Para evitar este problema, normaliza el String antes del switch con .toLowerCase() o .toUpperCase():
Ahora "Admin", "ADMIN", "admin" y "aDmIn" entran todos en el case "admin". Mucho más robusto.
3.6.3 Switch con String vs if/else if con .equals()
Comparemos las dos formas para el mismo problema:
El switch es más limpio y, además, el compilador sabe que estás comparando siempre contra el mismo rol (cosa que con if/else if no es obvia). En algunos casos, la JVM puede optimizar el switch con String usando un hashCode() interno, haciéndolo más eficiente que una cadena de .equals().
3.6.4 Switch con String y null
Aquí hay una sutileza importante: en el switch clásico, si el String es null, el programa lanza NullPointerException. No hay case null.
Para evitarlo, comprueba null antes del switch:
En Java 21+ existe case null en switch con pattern matching (sección 3.8), pero para switch con String "puro" hay que hacer la comprobación aparte.
3.6.5 Ejemplo: menú de opciones
Fíjate:
- Normalizamos la entrada con
.trim().toLowerCase(): el usuario puede escribir" Sumar ","SUMAR"o"sumar"y todas funcionan. - Cada
casees un bloque con sus propias variablesayb. Esto es legal porque cada bloque tiene su propio ámbito. defaultpara cualquier entrada no reconocida.
3.7 Switch con enum
3.7.1 Recordatorio: qué es un enum
Un enum (de enumeration) es un tipo especial de Java que sirve para definir un conjunto cerrado de constantes nombradas. Ya lo viste por encima en UD1 (sección 6): las constantes de un enum son valores fijos, escritos en MAYÚSCULAS, separados por comas.
Los enums se ven en UD4
Aquí solo usamos enums como "cajas negras" reconocibles. La definición completa de enums (con propiedades, constructores, métodos, etc.) se cubre en UD4, sección 6. Por ahora te basta saber que un enum es un conjunto de constantes y que se pueden usar en un switch igual que un int o un String.
3.7.2 Switch con enum: la mejor opción para conjuntos cerrados
El switch con enum es uno de los usos más elegantes. Como el compilador conoce todos los valores posibles del enum, puede comprobar que has cubierto todos los casos.
Salida:
3.7.3 Exhaustividad real con enum
Aquí viene lo potente: como el compilador sabe que DiaSemana tiene exactamente 7 valores, si te dejas uno, da error de compilación.
Error del compilador:
Esto es oro puro: el compilador te obliga a pensar en todos los casos. Si mañana añades un nuevo valor al enum (por ejemplo, FESTIVO), todos los switch que lo usan te marcan error hasta que los actualices. Es la fuerza del sistema de tipos trabajando a tu favor.
Si pones default en un switch con enum exhaustivo, IntelliJ te avisa de que es redundante. La mejor práctica es cubrir todos los casos explícitamente y no usar default:
3.7.4 No uses default con enums exhaustivos
Hay una regla no escrita en la comunidad Java:
Si usas un
enumcomo selector y cubres todos sus valores, no pongasdefault. Eldefaultsolo sirve para "tapar" casos no contemplados, y si elenumestá cerrado, no hay más casos.
Sin default, si mañana añades FESTIVO al enum, el compilador te obliga a actualizar el switch. Con default, el switch "se traga" el nuevo caso sin avisarte, y podrías tener un bug silencioso.
3.7.5 Ejemplo completo: turno de trabajo
Aquí tenemos dos switch expression sobre el mismo enum: uno devuelve un String, otro un double. Los dos son exhaustivos (cubren los tres valores del enum) y no necesitan default.
3.7.6 Enum del API de Java: java.time.DayOfWeek
En Java viene predefinido el enum java.time.DayOfWeek (lo verás en profundidad en UD2 cuando estudiemos java.time). Aunque pertenece al API que se profundiza en UD2, ya podemos usarlo aquí porque es "una caja negra con 7 valores":
Aviso: en java.time.DayOfWeek los valores están en inglés (MONDAY, TUESDAY, etc.). Es una de las pequeñas contradicciones del API de Java: la internacionalización (i18n) de los nombres va por otro lado (DateTimeFormatter), y los identificadores del enum siempre son en inglés.
3.8 Exhaustividad y default
3.8.1 ¿Qué es la exhaustividad?
La exhaustividad es la propiedad de un switch que cubre todos los casos posibles del selector. Java la exige en los switch expression (los que devuelven un valor) y la recomienda en los switch statement (los que ejecutan acciones).
| Tipo de switch | ¿Exige exhaustividad? |
|---|---|
Switch expression (var r = switch(...) { ... };) |
Sí, siempre. |
Switch statement con flecha (->) sobre int, char, String |
No obligatoria, pero recomendada con default. |
Switch statement con enum |
Recomendada: cubre todos los valores o pon default. |
3.8.2 ¿Cómo se logra la exhaustividad?
Hay dos formas:
- Cubriendo todos los valores del
enum(en switch conenum). - Poniendo
default(la forma universal).
Ejemplo de cada una:
Una tercera forma, reservada para UD7
Existe una tercera forma de lograr exhaustividad, basada en sealed classes y record (jerarquías cerradas de tipos). La verás en UD7 (POO Avanzada), cuando conozcas objetos, herencia y polimorfismo. Aquí no la tratamos porque necesita conceptos que aún no tienes.
3.8.3 default como "red de seguridad"
Para switch con int o String, el default es obligatorio en switch expression y recomendado en switch statement. Sirve para capturar valores que no has contemplado:
Sin default, este switch expression no compilaría porque el compilador no puede garantizar que dia esté entre 1 y 7 (es un int, puede valer cualquier cosa).
3.8.4 default con throw: el patrón "inesperado"
En código profesional, cuando el default no debería ejecutarse nunca (porque la lógica del programa garantiza que los demás casos cubren todo), se suele lanzar una excepción para avisar de que algo ha ido mal:
Si dia vale 8, en vez de imprimir "Día no válido", el programa lanza una excepción y se detiene con un mensaje claro. Las excepciones se ven en la sección 8; por ahora te basta saber que throw new IllegalArgumentException("mensaje") es la forma de decir "esto no debería haber pasado, aborta".
3.8.5 Diagrama de decisión: ¿necesito default?
flowchart TD
A["¿Es switch expression?"] -->|Sí| B["¿Selector es enum y cubres todos los valores?"]
A -->|No, es statement| C["Recomendado poner default"]
B -->|Sí| D["No hace falta default"]
B -->|No| E["Pon default o throw"]
C --> F["Fin"]
D --> F
E --> F
3.9 Cuándo usar switch y cuándo if
Ya tenemos dos herramientas de selección: if/else if/else (sección 2) y switch (esta sección). ¿Cuándo usar cada una? Esta tabla te lo decide en segundos:
| Criterio | Usa if |
Usa switch |
|---|---|---|
| Comparas el mismo valor contra muchos literales. | Funciona, pero verboso. | Mejor switch. |
Comparas rangos (edad >= 18 && edad <= 65). |
Mejor if. | Switch no admite rangos. |
| Comparas condiciones booleanas complejas. | Mejor if. | Switch no admite booleanos. |
| Quieres asignar/devolver un valor según una variable. | Funciona, pero verboso. | Mejor switch expression. |
Selector es boolean, long, float o double. |
Solo if. Switch no los admite. | No se puede. |
Selector es int, char, String o enum. |
Funciona. | Mejor switch. |
| Casos mutuamente excluyentes y pocos. | Cualquiera. | Cualquiera. |
Casos con condiciones compuestas (||, &&). |
Mejor if. | No aplica (todavía). |
Validación de entrada (< 0 || > 120). |
Mejor if. | No aplica. |
3.9.1 Reglas mnemotécnicas
"Switch para valores, if para condiciones."
Si la pregunta es "¿qué valor tiene esta variable?", usa switch.
Si la pregunta es "¿se cumple esta condición?", usa if.
"Switch cuando hay 3+ casos sobre el mismo selector."
Si tienes 3 o más else if comparando x == valor, cámbialo a switch. Es más limpio y el compilador te ayuda con la exhaustividad.
"Switch expression cuando el switch produce un valor."
Si el switch devuelve algo (un String, un int, un cálculo), usa switch expression. Si solo hace algo (imprimir, modificar estado), usa switch statement.
3.9.2 Ejemplo: refactorización de if a switch
Vamos a refactorizar un código con if a switch para ver la diferencia:
Antes (con if/else if):
Después (con switch expression):
De 16 líneas a 9. Y, sobre todo, más legible: el switch deja claro que estamos clasificando dia, mientras que el if/else if repite dia == una y otra vez.
3.9.3 Cuándo if sigue siendo mejor
Hay casos en los que el if no tiene rival:
Aquí no puedes usar switch porque:
- Los casos no son literales (son rangos como
edad < 18). - El selector no es un valor único, son condiciones booleanas.
Por eso ambas herramientas coexisten: if para condiciones, switch para valores.
3.10 Errores habituales al usar switch
Antes de pasar a los ejemplos, repasemos los errores más comunes para que no caigas en ellos.
3.10.1 Error 1: olvidar break en el switch clásico
Ya lo vimos en 3.2.3: el fall-through accidental es el bug más típico del switch clásico.
Solución: usa siempre la sintaxis moderna con flecha ->. Si te ves obligado a mantener código clásico, pon break al final de cada case y revisa dos veces.
3.10.2 Error 2: olvidar default en switch expression
Solución: en switch expression, siempre cubre todos los casos o pon default. IntelliJ lo marca en rojo.
3.10.3 Error 3: null en switch con String
Solución: comprueba null antes del switch con un if. (Existe case null con pattern matching, pero se ve en UD7.)
3.10.4 Error 4: no normalizar String antes del switch
Solución: .toLowerCase() o .toUpperCase() antes del switch, o usa equalsIgnoreCase con if/else if.
3.10.5 Error 5: rangos en switch
Solución: para rangos, usa if. Si insistes en switch, tienes que enumerar todos los valores (case 0, 1, 2, 3, ..., 17 -> ...), lo cual es absurdo.
3.10.6 Error 6: default en switch con enum exhaustivo
Solución: si cubres todos los valores del enum, quita el default. Así, si mañana añades un valor nuevo al enum, el compilador te obliga a actualizar el switch.
3.10.7 Error 7: mezclar : y -> en el mismo switch
Solución: usa una sola sintaxis por switch. Y preferiblemente, siempre la moderna (->).
3.11 Ejemplos resueltos paso a paso
Cuatro ejemplos completos que integran todo lo aprendido. Te recomiendo escribirlos en IntelliJ y jugar con los valores.
3.11.1 Ejemplo 1: día de la semana con switch expression
Salidas posibles:
| Entrada | Salida |
|---|---|
1 |
Hoy es Lunes, un día laborable. |
6 |
Hoy es Sábado, un día fin de semana. |
9 |
Hoy es Día no válido, un día desconocido. |
Puntos clave:
- Dos switch expression sobre el mismo selector: una para el nombre, otra para el tipo.
- Múltiples etiquetas para agrupar laborables y fin de semana.
defaultobligatorio porquediaes uninty el compilador no sabe que está entre 1 y 7.- Sin
import:java.lang.IOestá disponible por defecto.
3.11.2 Ejemplo 2: calculadora con switch expression
Reescribimos la calculadora de la sección 2 usando switch expression.
Puntos clave:
- Switch expression con bloques
{ ... yield ...; }porque cada rama imprime un mensaje y además devuelve un valor. - Validación de división por cero dentro del case 4 con un
ifinterno. Como estamos dentro de un bloque, podemos escribir varias sentencias. yielddevuelve el valor de cada rama.- El switch produce un
Stringque se asigna aresultadoy se imprime al final.
3.11.3 Ejemplo 3: tarifa según tipo de vehículo (con enum)
Puntos clave:
- Enum propio definido fuera de la clase (sintaxis mínima, sin profundizar — UD4).
- Switch expression sin
default: como el enum está cerrado y cubrimos los 5 valores, el compilador garantiza exhaustividad. - Cada rama produce un
double: el switch expression devuelve el valor de la tarifa. - Si mañana añadiéramos
FURGONETAal enum, el compilador daría error hasta que añadamos uncase FURGONETA -> .... Eso es la fuerza de la exhaustividad con enums trabajando a tu favor.
3.11.4 Ejemplo 4: menú interactivo con switch statement
Aquí un switch statement (no expression) que ejecuta acciones, no produce un valor:
Puntos clave:
- Switch statement con flecha (no expression): no se asigna a ninguna variable.
- Bloques
{ }porque cada rama tiene varias sentencias. - Cada bloque puede declarar sus propias variables locales (
nombre), independientes entre sí.
3.12 Buenas prácticas del switch
3.12.1 Las 8 reglas del switch profesional
- Usa la sintaxis moderna con flecha
->. No escribas switch clásico a menos que mantengas código heredado. - Switch expression cuando produces un valor. Switch statement cuando solo ejecutas acciones.
- Múltiples etiquetas (
case 1, 2, 3 ->) en vez de fall-through. - Cubre todos los casos en switch expression: o todos los valores del enum, o
default. - No pongas
defaultcon enum exhaustivo. Deja que el compilador te avise si falta un caso. - Normaliza
Stringantes del switch con.toLowerCase()o.toUpperCase(). - Comprueba
nullantes del switch conString(con unifprevio). throwendefaultinesperado para detectar bugs:default -> throw new IllegalArgumentException(...).
3.12.2 Cuándo refactorizar
| Síntoma | Refactoriza a |
|---|---|
5+ else if comparando el mismo valor. |
switch expression. |
Switch clásico con muchos break. |
Switch moderno con flecha. |
Switch con case null que no compila (clásico). |
if previo para null. |
default con enum exhaustivo. |
Quitar default y cubrir todos los casos. |
3.12.3 Comentar el switch
Los comentarios buenos en un switch explican decisiones de diseño, no lo obvio:
3.13 Actividades propuestas
Cinco actividades graduadas. Hazlas en orden.
3.13.1 Actividad 1 — Refactoriza de if a switch
Objetivo: practicar la refactorización de if/else if a switch expression.
Enunciado: Toma el siguiente código (que usa if/else if) y reescríbelo con switch expression:
Requisitos:
- Usa switch expression con flecha
->. - Incluye
default. - Asigna el resultado a
String nombreMes.
Entrega: archivo RefactorMeses.java en el paquete jams.programacion.ud3.
Criterios evaluados: CE 3.1, CE 3.5, CE 3.7.
3.13.2 Actividad 2 — Calculadora de tarifa de aparcamiento
Objetivo: practicar switch expression con bloques y yield.
Enunciado: Una app de aparcamiento cobra:
| Tipo de vehículo | Tarifa (€/h) |
|---|---|
COCHE |
2.50 |
MOTO |
1.20 |
AUTOBUS |
6.00 |
CAMION |
8.50 |
BICICLETA |
0.00 (gratis) |
Requisitos:
- Define un enum
Vehiculocon los cinco valores (fuera de la clasemain). - Pregunta al usuario el tipo de vehículo (1 = COCHE, 2 = MOTO, ... 5 = BICICLETA).
- Pregunta el número de horas (admite decimales).
- Usa un switch statement sobre
intpara convertir la opción numérica en un valor del enum (condefault -> throwpara opción inválida). - Usa un switch expression sobre el enum para obtener la tarifa por hora.
- Calcula el total y muéstralo con
String.format("%.2f", total). - Lee todo con
IO.readln().
Entrega: archivo TarifaAparcamiento.java en el paquete jams.programacion.ud3. Incluye en comentarios al final tres ejemplos de ejecución.
Criterios evaluados: CE 3.1, CE 3.5, CE 3.7.
3.13.3 Actividad 3 — Conversor de puntuación a letra
Objetivo: practicar switch con rangos simulados (usando if interno) y comparativa con if/else if.
Enunciado: Escribe un programa ConversorNota que pida al usuario una nota numérica (0.0 a 10.0) y devuelva su letra (A, B, C, D, F) según la tabla del ejemplo 2.8.4 de la sección 2.
Tareas:
- Versión A (con
if/else if): copia la solución del ejemplo 2.8.4. - Versión B (con switch): como el switch no admite rangos, usa un truco: trunca la nota a un
intcon(int) notay hazswitchsobre eseint, agrupando los casos. Por ejemplo:case 9, 10 -> "A",case 7, 8 -> "B", etc. (Cuidado con los decimales:nota = 7.9truncada es7, que va a"B". ¿Es correcto?) - Compara ambas versiones: ¿cuál es más legible? ¿Cuál maneja mejor los decimales? Escribe tus conclusiones en un comentario al final.
Requisitos:
- Lee la nota con
IO.readln().trim().replace(',', '.').
Entrega: archivo ConversorNota.java con ambas versiones (la B activa, la A en un comentario de bloque).
Criterios evaluados: CE 3.1, CE 3.5, CE 3.7.
3.13.4 Actividad 4 — Cuestionario de repaso
Responde por escrito:
- ¿Cuál es la diferencia principal entre
switchclásico yswitchcon flecha->? - ¿Qué es el fall-through y por qué es peligroso?
- ¿Cuándo se usa
yielden un switch expression? - ¿Por qué el switch expression obliga a ser exhaustivo?
- ¿Por qué no es buena idea poner
defaulten un switch con enum si ya cubres todos los valores? - Escribe con múltiples etiquetas: "los días 6 y 7 son fin de semana".
- ¿Qué pasa si haces
switchsobre unStringque esnull? - ¿Por qué
switch (edad) { case 0..17 -> ...; }no compila en Java? - ¿Qué hace
default -> throw new IllegalArgumentException(...)? - ¿Cuándo eliges
ify cuándoswitch? Pon dos ejemplos de cada uno. - ¿Por qué conviene normalizar un
StringcontoLowerCase()antes de un switch? - ¿Por qué el
switchconboolean,long,floatydoubleno está permitido?
Entrega: archivo cuestionario_seccion3.txt con tus respuestas.
Criterios evaluados: CE 3.1, CE 3.7.
3.15 Lo que NO hemos visto (y dónde verlo)
Esta sección te ha enseñado el switch moderno en su versión básica: switch clásico, switch con flecha, switch expression, múltiples etiquetas, switch con String y switch con enum. Con eso ya puedes escribir código profesional para clasificar por valor.
Hay tres cosas relacionadas con el switch que no hemos visto y que se reservan para unidades posteriores, porque necesitan conceptos que todavía no tienes:
| Concepto | ¿Por qué no aquí? | ¿Dónde se ve? |
|---|---|---|
Switch con pattern matching (case Integer i -> ...) |
Necesita entender Object, autoboxing, jerarquía de tipos y casting. |
UD7 (POO Avanzada). |
Guards con when (case Integer i when i > 0 -> ...) |
Extensión del pattern matching en switch. | UD7 (POO Avanzada). |
Exhaustividad con sealed classes y record |
Necesita conocer herencia, interfaces, sealed y records. | UD4 (intro) y UD7 (desarrollo). |
case null |
Forma parte del pattern matching en switch. | UD7 (POO Avanzada). |
Cuando llegues a UD7, después de haber visto UD2 (POO con la API de Java) y UD4 (Clases y Objetos Propios), tendrás todas las herramientas para entender el pattern matching en switch de una sola vez y en profundidad. Esa es la razón por la que lo hemos dejado aparcado: aprenderlo ahora, sin saber qué es un objeto, sería copiar código sin entenderlo.