Control de flujo de Java: la declaración de cambio

Las declaraciones condicionales y los bucles son una herramienta muy importante en la programación. No hay muchas cosas que podamos hacer con un código que solo puede ejecutarse línea por línea...

Introducción

Las declaraciones condicionales y los bucles son una herramienta muy importante en la programación. No hay muchas cosas que podamos hacer con un código que solo puede ejecutarse línea por línea.

Eso es lo que significa "control de flujo": guiar la ejecución de nuestro programa, en lugar de dejar que se ejecute línea por línea, independientemente de cualquier factor interno o externo. Cada lenguaje de programación admite alguna forma de control de flujo, si no explícitamente a través de ifs y fors o declaraciones similares, implícitamente nos brinda las herramientas para crear tales construcciones, es decir, los lenguajes de programación de bajo nivel generalmente logran ese efecto con una gran cantidad de comandos go-to.

Los bucles eran un concepto utilizado mucho antes de que existiera la programación informática, pero la primera persona en utilizar un bucle de software fue Ada Lovelace, comúnmente conocida por su apellido de soltera - Byron, mientras calculaba [Números de Bernoulli](https://en .wikipedia.org/wiki/Bernoulli_number), allá por el siglo XIX.

En Java, hay varias formas de controlar el flujo del código:

La declaración switch

Si deseamos comparar un valor con varios valores y ejecutar código en función de su igualdad, podríamos hacer algo como:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
String input = "someCommand";

if (input.equals("date")) {
    System.out.println("The current date is: " + new Date());
} else if (input.equals("help")) {
    System.out.println("The possible commands are...");
} else if (input.equals("list")) {
    System.out.println("The files in this directory are...");
} else if (input.equals("exit")) {
    System.out.println("Exiting application...");
} else {
    System.out.println("Command Not Supported");
}

Esto puede volverse rápidamente engorroso e ilegible. La instrucción switch se nos presentó precisamente para evitar esto siempre que sea posible. Esta declaración se usa cuando tenemos varias cosas diferentes que queremos ejecutar en función del valor de una sola expresión:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
switch(variable) { 
    case constant1:
        // Do something if the variable is equal to constant1. 
        // constant1 must be of same type as variable
        // or easily converted to, such as Integer -> int
        break; 
    case constant2:
        // Some code
        break; 
    ... 
    default: 
        // Some code
        break;
}

La variable pasada como argumento switch es la variable o expresión cuyo valor estamos comparando. Ese valor se compara con cada uno de los valores case. Si la variable que estamos comprobando coincide con alguno de los casos, se ejecuta el código que sigue a ese caso. Podemos tener tantas sentencias case como queramos.

Hay cuatro palabras clave nuevas que se nos presentan aquí: switch, case, break y default.

cambiar

La sentencia switch normalmente acepta una variable, aunque también puede aceptar una expresión si devuelve un tipo aceptado:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Comparing the value 5 with case values
switch(5) {
    // Cases
}

int x = 5;
int y = 10;

// Comparing the value 15 with case values
switch(x+y) {
    // Cases
}

// Booleans are not supported by switch statements, 
// so this won't compile
switch(!true) {
    // Cases
}

Si pasamos un valor null al switch, surgirá una NullPointerException.

caso

El valor de la etiqueta case debe ser una constante de tiempo de compilación. Esto significa que para todos los valores case, debemos usar literales/constantes (es decir, "abc", 5, etc.) o variables que se han declarado final y se les ha asignado un valor:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
final int i = 5;
int y = 15;
final int z;
z = 25;

int x = 10;
switch(x) {
    case i:
        // i can't be changed at any point due to the
        // `final` modifier, will compile
        break;
    case y:
        // Won't compile as `y` isn't a compile-time constant
        break;
    case 10+10:
        // Compiles, as 10+10 is a compile-time constant
        break;
    case z:
        // Doesn't compile as z wasn't initialized with
        // its declaration, thus it isn't considered to
        // be a compile-time constant
        break;
    case null:
        // Doesn't compile, there can't be a null case
        break;
}

Todos los casos deben ser únicos, de lo contrario, el código Java no se compilará. Si los casos no fueran únicos, no habría forma de saber qué caso ejecutar entre los valores duplicados.

descanso

La palabra clave break se usa para interrumpir el flujo del código dentro de los casos. Una vez que la instrucción switch ha encontrado un caso que coincide con la variable pasada, procede a ejecutar el código del caso hasta que se encuentra la primera palabra clave break o el final del propio bloque switch.

Por ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int ourNumber = 1;

switch(ourNumber) {
    case 1:
        System.out.println("One");
    case 2:
        System.out.println("Two");
    case 3:
        System.out.println("Three");
        break;
    case 4:
        System.out.println("Four");
}

La salida de este código es:

1
2
3
One
Two
Three

En este ejemplo, el flujo de ejecución "falla" desde la primera declaración del caso (que coincidió con el valor ourNumber) hasta la declaración break.

Esto a veces puede llevar a que se ejecuten casos no deseados, por lo que debemos tener cuidado de agregar sentencias break, y una buena idea es agregarlas por defecto en cada caso, y eliminarlas más tarde si queremos ejecutar múltiples casos para algunas entradas. Puede encontrar más información sobre la sentencia break aquí.

Si queríamos que se ejecutara el mismo código para varios casos, este es un patrón común que se usa:

1
2
3
4
5
6
...
case "a":
case "b":
    System.out.println("Variable is equal to constant1 or constant2!");
    break;
...

Esto ejecutaría la línea System.out.println si nuestra variable es igual a "a" o si es igual a "b".

defecto

El caso default es una pieza de código que switch ejecuta si la variable que le hemos pasado no coincide con ninguno de los casos dados. Esta declaración es opcional, aunque muy recomendable para evitar situaciones en las que la entrada definida por el usuario interrumpa todo el flujo de la aplicación.

Tipos de datos aceptados

La ‘variable’ pasada como argumento de ‘interruptor’ puede ser una de las siguientes:

  • char
  • byte
  • corto
  • En t
  • Entero
  • Corto
  • byte
  • Personaje
  • enumeración
  • Cuerda

Este es un gran salto desde las declaraciones if y else-if, que solo admiten expresiones booleanas.

Dicho esto, podemos reescribir fácilmente el ejemplo if/if-else del principio de este artículo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 switch("someCommand") {
     case "date":
         System.out.println("The current date is: " + new Date());
         break;
     case "help":
         System.out.println("The possible commands are...");
         break;
     case "list":
         System.out.println("The files in this directory are...");
         break;
     case "exit":
         System.out.println("Exiting application...");
         break;
     default:
         System.out.println("Command Not Supported");
         break;
 }

O, como otro ejemplo, podríamos pasar un número entero y reemplazar esta cadena de bloques if y else-if con una contraparte más legible:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
int ourNumber = 4;

if (ourNumber == 1) {
    System.out.println("One");
}
else if (ourNumber == 2) {
    System.out.println("Two");
}
else if (ourNumber == 3) {
    System.out.println("Three");
}
else if (ourNumber == 4) {
    System.out.println("Four");
}
else {
    System.out.println("Larger than 4");
}

La declaración switch equivalente sería similar a la siguiente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
switch(ourNumber) {
    case 1:
        System.out.println("One");
        break;
    case 2:
        System.out.println("Two");
        break;
    case 3:
        System.out.println("Three");
        break;
    case 4:
        System.out.println("Four");
        break;
    default:
        System.out.println("Larger than 4");
        break;
}

Está claro que si ourNumber es igual a 1, el código después de case 1: se ejecutará y "One" se imprimirá en la salida estándar.

Nota: A diferencia de otros tipos de datos, las cadenas no se comparan con el operador ==, sino con el método .equals() cuando se verifican todos los casos. Esto se hace para que se compare el valor real de las cadenas y no la ubicación de la memoria.

Declaraciones anidadas switch

Podemos anidar varias sentencias switch.

1
2
3
4
5
6
7
8
9
switch(var1) {
    case constant1:
        switch(var2) {
            // ...
        }
        break;
    case constant2:
    ...
}

Expresiones lambda {#expresiones lambda}

Java 12 tiene una forma nueva y más concisa de manejar sentencias switch usando expresiones Lambda.

Al utilizar las sentencias switch de esta manera, los descansos ya no son necesarios y la devolución de un valor es más legible. Esto solo está disponible como una opción de vista previa actualmente. Un nuevo tipo típico de interruptor se vería así:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
switch(ourNumber) {
    case 7, 3, 8, 4 -> System.out.println("Very popular lucky number");
    case 5, 13, 9, 6 -> System.out.println("Kind of popular lucky number");
    default -> System.out.println("Not all that popular");
}

// Or something like:
String s = switch(ourNumber) {
    case 7, 3, 8, 4 -> "Very popular lucky number";
    // ...
}
System.out.println(s);

Conclusión

El control de flujo en el código es esencial para absolutamente todas las aplicaciones. Las declaraciones que alteran el flujo del código son bloques de construcción fundamentales y cada aspirante a desarrollador debe tener el control completo y ser consciente de cómo funcionan.

Licensed under CC BY-NC-SA 4.0