1. Información general
En este tutorial, aprenderemos qué es la declaración de cambio y cómo usarla.
La declaración de cambio nos permite reemplazar varias construcciones if-else anidadas y así mejorar la legibilidad de nuestro código.
Switch ha evolucionado con el tiempo; se han agregado nuevos tipos compatibles, particularmente en Java 5 y 7. Además, continúa evolucionando; las expresiones de switch probablemente se introducirán en Java 12.
A continuación, daremos algunos ejemplos de código para demostrar el uso de la instrucción switch , el papel de la instrucción break , los requisitos para los valores de argumento / caso de switch y la comparación de String s en una instrucción switch .
Pasemos al ejemplo.
2. Ejemplo de uso
Digamos que tenemos las siguientes declaraciones if-else anidadas :
public String exampleOfIF(String animal) { String result; if (animal.equals("DOG") || animal.equals("CAT")) { result = "domestic animal"; } else if (animal.equals("TIGER")) { result = "wild animal"; } else { result = "unknown animal"; } return result; }
El código anterior no se ve bien y sería difícil de mantener y razonar. Para mejorar la legibilidad, podríamos hacer uso de una declaración de cambio aquí:
public String exampleOfSwitch(String animal) { String result; switch (animal) { case "DOG": result = "domestic animal"; break; case "CAT": result = "domestic animal"; break; case "TIGER": result = "wild animal"; break; default: result = "unknown animal"; break; } return result; }
Como se muestra arriba, comparamos el argumento switch animal con varios valores de casos . Si ninguno de los valores del caso es igual al argumento, se ejecuta el bloque bajo la etiqueta predeterminada .
En pocas palabras, la sentencia break se utiliza para salir de una sentencia switch .
3. La declaración de descanso
Aunque la mayoría de las sentencias de cambio en la vida real implican que solo se debe ejecutar uno de los bloques de casos , la sentencia de interrupción es necesaria para salir de un cambio después de que se completa el bloque.
Si nos olvidamos de escribir una ruptura , se ejecutarán los bloques de debajo.
Para demostrar esto, omitamos las declaraciones de interrupción y agreguemos la salida a la consola para cada bloque:
public String forgetBreakInSwitch(String animal) { switch (animal) { case "DOG": System.out.println("domestic animal"); default: System.out.println("unknown animal"); } }
Ejecutemos este código olvideBreakInSwitch ( "PERRO"), y verifiquemos la salida para demostrar que todos los bloques se ejecutan:
domestic animal unknown animal
Por lo tanto, debemos tener cuidado y agregar declaraciones de interrupción al final de cada bloque, a menos que sea necesario pasar al código debajo de la siguiente etiqueta.
El único bloque donde no es necesaria una ruptura es el último, pero agregar una ruptura al último bloque hace que el código sea menos propenso a errores.
También podemos aprovechar este comportamiento para omitir break cuando queremos que se ejecute el mismo código para varias sentencias de caso. Reescribamos el ejemplo de la sección anterior agrupando los 2 primeros casos:
public String exampleOfSwitch(String animal) { String result; switch (animal) { case "DOG": case "CAT": result = "domestic animal"; break; case "TIGER": result = "wild animal"; break; default: result = "unknown animal"; break; } return result; }
4. cambiar Argumento y valores de caso
Ahora analicemos los tipos permitidos de argumentos de cambio y valores de caso , los requisitos para ellos y cómo funciona la instrucción de cambio con cadenas.
4.1. Tipos de datos
No podemos comparar todos los tipos de objetos y primitivas en la declaración de cambio . Un conmutador funciona solo con cuatro primitivas y sus envoltorios, así como con el tipo enum y la clase String :
- byte y Byte
- corto y corto
- int y Integer
- char y carácter
- enumeración
- Cuerda
El tipo de cadena está disponible en la instrucción switch que comienza con Java 7.
El tipo enum se introdujo en Java 5 y ha estado disponible en la declaración de cambio desde entonces.
Las clases de envoltura también han estado disponibles desde Java 5.
Por supuesto, los valores de caso y argumento de cambio deben ser del mismo tipo.
4.2. Sin valores nulos
No podemos pasar el valor nulo como argumento a una declaración de cambio . Si lo hacemos, el programa lanzará NullPointerException, usando nuestro primer ejemplo de cambio :
@Test(expected=NullPointerException.class) public void whenSwitchAgumentIsNull_thenNullPointerException() { String animal = null; Assert.assertEquals("domestic animal", s.exampleOfSwitch(animal)); }
Por supuesto, tampoco podemos pasar nulo como valor a la etiqueta de caso de una declaración de cambio . Si lo hacemos, el código no se compilará.
4.3. Valores de caso como constantes en tiempo de compilación
Si intentamos reemplazar el valor del caso DOG con la variable dog, el código no se compilará hasta que marquemos la variable dog como final :
final String dog="DOG"; String cat="CAT"; switch (animal) { case dog: //compiles result = "domestic animal"; case cat: //does not compile result = "feline" }
4.4. Comparación de cadenas
Si una declaración de cambio usaba el operador de igualdad para comparar cadenas, no podríamos comparar un argumento de cadena creado con el nuevo operador con un valor de caso de cadena correctamente.
Afortunadamente, el operador del conmutador usa el método equals () debajo del capó .
Demostremos esto:
@Test public void whenCompareStrings_thenByEqual() { String animal = new String("DOG"); assertEquals("domestic animal", s.exampleOfSwitch(animal)); }
5. Cambiar Expresiones
JDK 13 is now available and brings an improved version of a new feature first introduced in JDK 12: the switch expression.
In order to enable it, we need to pass –enable-preview to the compiler.
5.1. The New switch Expression
Let's see what the new switch expression looks like when switching over months:
var result = switch(month) { case JANUARY, JUNE, JULY -> 3; case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> 1; case MARCH, MAY, APRIL, AUGUST -> 2; default -> 0; };
Sending in a value like Month.JUNE would set result to 3.
Notice that the new syntax uses the ->operator instead of the colon we're used to with switch statements. Also, there's no break keyword: The switch expression doesn't fall through cases.
Another addition is the fact that we can now have comma-delimited criteria.
5.2. The yield Keyword
Going a bit further, there's a possibility to obtain fine-grained control over what's happening on the right side of the expression by using code blocks. In such a case, we need to use the keyword yield:
var result = switch (month) { case JANUARY, JUNE, JULY -> 3; case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> 1; case MARCH, MAY, APRIL, AUGUST -> { int monthLength = month.toString().length(); yield monthLength * 4; } default -> 0; };
While our example is a bit arbitrary, the point here is that we've got access to more of the Java language here.
5.3. Returning Inside switch Expressions
As a consequence of the distinction between switch statements and switch expressions, it is possible to return from inside a switch statement, but we're not allowed to do so from within a switch expression.
The following example is perfectly valid and will compile:
switch (month) { case JANUARY, JUNE, JULY -> { return 3; } default -> { return 0; } }
However, the following code will not compile, as we are trying to return outside of an enclosing switch expression:
var result = switch (month) { case JANUARY, JUNE, JULY -> { return 3; } default -> { return 0; } };
5.4. Exhaustiveness
When using switch statements, it doesn't really matter if all cases are covered.
The following code, for example, is perfectly valid and will compile:
switch (month) { case JANUARY, JUNE, JULY -> 3; case FEBRUARY, SEPTEMBER -> 1; }
For switch expressions though, the compiler insists that all possible cases are covered. The following code snippet, for example, would not compile, as there's no default case and not all possible cases are covered:
var result = switch (month) { case JANUARY, JUNE, JULY -> 3; case FEBRUARY, SEPTEMBER -> 1; }
The switch expression, however, will be valid when all possible cases are covered, like in the following example:
var result = switch (month) { case JANUARY, JUNE, JULY -> 3; case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> 1; case MARCH, MAY, APRIL, AUGUST -> 2; }
Please note that the above code snippet does not have a default case. As long as all cases are covered, the switch expression will be valid.
6. Conclusion
En este tutorial, hemos aprendido las sutilezas del uso de la instrucción switch en Java. Podemos decidir si usar el interruptor en función de la legibilidad y el tipo de valores comparados.
La declaración de cambio es un buen candidato para los casos en los que tenemos un número limitado de opciones en un conjunto predefinido (por ejemplo: días de la semana). De lo contrario, tendríamos que modificar el código cada vez que se agregue o elimine un nuevo valor, lo que puede no ser factible. Para estos casos, deberíamos considerar otros enfoques como el polimorfismo u otros patrones de diseño como Command.
Como siempre, el código JDK 8 completo y el código JDK 13 están disponibles en GitHub.