Bienvenido, soy Miguel y para hoy les traigo un nuevo artículo.
Índice
Con muchos ejemplos
Java 14 se lanzará el 17 de marzo de 2020. La nueva versión de Java contiene una interesante actualización del lenguaje Java: nuevas expresiones switch
. Veamos cómo en switch
se pueden usar las nuevas expresiones, qué tipo de ventajas ofrecen y qué puede salir mal. Al final, abordaremos una pregunta delicada sobre las expresiones switch
.
La declaración clásica de «Switch»
El diseño actual de la declaración switch
en Java sigue lenguajes como C y C ++. Funciona solo como una declaración y admite la semántica fallida de forma predeterminada. Aquí hay un ejemplo de la declaración switch
clásica con una enumeración:
public class ClassicSwitchStatement { enum Person { Mozart, Picasso, Goethe, Dostoevsky, Prokofiev, Dali } public static void main(String[] args) { print(Person.Mozart); print(Person.Dali); print(Person.Dostoevsky); } static void print(Person person) { switch (person) { case Dali: case Picasso: System.out.printf("%s was a painter%n", person); break; case Mozart: case Prokofiev: System.out.printf("%s was a composer%n", person); break; case Goethe: case Dostoevsky: System.out.printf("%s was a writer%n", person); break; default: throw new IllegalArgumentException( String.format("Unknown person: %s", person)); } } }
Es posible que haya notado muchas declaraciones de casos y rupturas en el ejemplo anterior. Esas declaraciones introducen algo de ruido visual y hacen que el código sea innecesariamente detallado. Este ruido visual puede enmascarar errores como una declaración de ruptura faltante, lo que significaría una caída accidental.
Las nuevas expresiones «Switch»
Java 14 se extiende switch
para que pueda usarse como una declaración o una expresión. En particular, el nuevo Java presenta lo siguiente:
- Una nueva forma de la etiqueta
switch
case ... ->
, donde solo se ejecutará el código a la derecha de la etiqueta si la etiqueta coincide. El código a la derecha de una etiquetacase ... ->
puede ser una expresión, un bloque o una declaración de lanzamiento. - Una nueva declaración
yield
para producir un valor que se convierte en el valor de la expresiónswitch
adjunta. - Varias constantes por caso que están separadas por comas.
Con el nuevo Java 14, es posible utilizar tanto las etiquetas case ... :
tradicionales como las nuevas case ... ->
. Es importante que las etiquetas tradicionales sigan siendo compatibles de forma predeterminada, pero no es necesario que las nuevas.
Veamos cómo se puede reescribir el ejemplo anterior con las nuevas expresiones switch
:
public class WhoIsWho { enum Person { Mozart, Picasso, Goethe, Dostoevsky, Prokofiev, Dali } public static void main(String[] args) { print(Person.Mozart); print(Person.Dali); print(Person.Dostoevsky); } static void print(Person person) { String title = switch (person) { case Dali, Picasso -> "painter"; case Mozart, Prokofiev -> "composer"; case Goethe, Dostoevsky -> "writer"; }; System.out.printf("%s was a %s%n", person, title); } }
Este sencillo ejemplo se puede compilar y ejecutar con un solo comando (gracias a JEP 330, que ha permitido el lanzamiento de programas de código fuente de un solo archivo desde Java 11):
$ java WhoIsWho.java Mozart was a composer Dali was a painter Dostoevsky was a writer
Puede ver que se han fusionado varias declaraciones de casos y que el código ya no usa una declaración de interrupción. Como resultado, el método print()
se volvió mucho más corto y se ve mejor.
El siguiente ejemplo muestra un bloque default
de varias líneas que usa la nueva declaración yield
para producir un valor. Tenga en cuenta que hay varias constantes nuevas en la enumeración Person
que no están cubiertas por las etiquetas case
de la expresión switch
:
public class WhoIsWho { enum Person { Mozart, Picasso, Goethe, Dostoevsky, Prokofiev, Dali, Gaudi, Bach, Einstein } public static void main(String[] args) { print(Person.Mozart); print(Person.Dali); print(Person.Einstein); } static void print(Person person) { String title = switch (person) { case Dali, Picasso -> "painter"; case Mozart, Prokofiev -> "composer"; case Goethe, Dostoevsky -> "writer"; default -> { System.out.printf("Oops! I don't know about %s%n", person); yield "..."; } }; System.out.printf("%s was a %s%n", person, title); } }
Esto es lo que imprime el programa:
$ java WhoIsWho.java Mozart was a composer Dali was a painter Oops! I don't know about Einstein Einstein was a ...
El siguiente ejemplo muestra cómo factorial
se puede implementar con las nuevas expresiones switch
:
static int factorial(int n) { return switch (n) { case 0, 1 -> 1; case 2 -> 2; default -> factorial(n - 1) * n; }; }
Detalles importantes
Hay varias cosas importantes que debe saber sobre el nuevo switch
Expresiones
Lo primero que hay que saber son los casos de switch
La expresión debe ser exhaustiva. En otras palabras, para todos los valores posibles, debe haber una coincidencia de la etiqueta switch
. Simplemente agreguemos un nuevo elemento a la enumeración y veamos qué va a pasar:
public class InvalidWhoIsWho { enum Person { Mozart, Picasso, Goethe, Dostoevsky, Prokofiev, Dali, // this element is not covered // by any case in the switch expression below // which results to compilation failure Gaudi } public static void main(String[] args) { print(Person.Mozart); print(Person.Dali); print(Person.Dostoevsky); } static void print(Person person) { String title = switch (person) { case Dali, Picasso -> "painter"; case Mozart, Prokofiev -> "composer"; case Goethe, Dostoevsky -> "writer"; }; System.out.printf("%s was a %s%n", person, title); } }
La compilación fallará de inmediato con el siguiente mensaje de error:
$ java InvalidWhoIsWho.java InvalidWhoIsWho.java:19: error: the switch expression does not cover all possible input values String title = switch (person) { ^ 1 error error: compilation failed
Añadiendo un simple default
case hace feliz al compilador de Java:
String title = switch (person) { case Dali, Picasso -> "painter"; case Mozart, Prokofiev -> "composer"; case Goethe, Dostoevsky -> "writer"; default -> "..."; };
En general, a menos que se utilice una enumeración y los casos de una switch
expresión cubre todas las constantes, una default
se requiere una cláusula en el switch
expresión.
La segunda cosa a recordar es switch
expresión debe completarse normalmente con un valor o lanzando una excepción. Echemos un vistazo al siguiente código:
public class InvalidSwitchExpressionWithoutDefault { public static void main(String[] args) { System.out.println(print(1)); } static String print(int n) { return switch (n) { case 0 -> "zero"; case 1 -> "one"; case 2 -> "two"; }; } }
Si intentamos compilar este código, el compilador de Java se quejará inmediatamente:
InvalidSwitchExpressionWithoutDefault.java:8: error: the switch expression does not cover all possible input values return switch (n) { ^ 1 error error: compilation failed
Nuevamente, agregando un default
case lo hace funcionar:
static String print(int n) { return switch (n) { case 0 -> "zero"; case 1 -> "one"; case 2 -> "two"; default -> "many"; }; }
La tercera cosa importante a tener en cuenta es yield
ahora es un identificador restringido. En particular, significa que las clases denominadas yield
convertirse en ilegal:
$ cat YieldClassName.java class yield {} $ javac YieldClassName.java YieldClassName.java:1: error: 'yield' not allowed here class yield { ^ as of release 13, 'yield' is a restricted type name and cannot be used for type declarations 1 error error: compilation failed
Sin embargo, está permitido usar yield
como una variable o un nombre de método:
$ cat ValidUseOfYieldWord.java public class ValidUseOfYieldWord { void yield() { int yield = 0; } } $ javac ValidUseOfYieldWord.java && echo ok || echo failed ok
Conclusión
Esto es lo que dicen los autores sobre las nuevas expresiones switch
:
«Estos cambios simplificarán la codificación diaria».
Veamos.
Prima
Qué piensas que va a pasar? Estas son las opciones:
- Error de compilación.
- Error de tiempo de ejecución.
Oops
se imprime.OopsOops
está impreso.Forty-two
se imprime.
public class StrangeYield { public static void main(String[] args) { go(0); } static void go(int n) { System.out.println( switch (n) { case 42 -> "Fotry-two"; default -> { yield("Oops"); } } ); } static String yield(String s) { return s + "Oops"; } }
Recursos
Añadir comentario