Gestión de errores con Either o Try en vez de con código de error, null, Optional, checked exception o unchecked exception

Escrito por el .
java planeta-codigo programacion
Enlace permanente Comentarios

A lo largo del tiempo han surgido varias formas de gestionar las excepciones. En C hace muchos años eran con códigos de error, en Java se incorporaron en el lenguaje las excepciones checked o unchecked o la nueva clase Optional en Java cada una con sus ventajas y y algunas deficiencias. Más recientemente usando un tipo tal que Either<L,R> son otra forma para el tratamiento de errores sobre las opciones anteriores.

Java

Una parte importante para el correcto funcionamiento de un programa corresponde a la gestión de errores que pueden producirse en su ejecución. Si se trata de un programa que se comunica vía interfaz de red ha de estar preparado ante la situación que la conexión se pierda o se produzcan errores en la transmisión porque por ejemplo se ha desconectado el cable de red o la WiFi no es estable. Si se trata de un programa que guarda datos en el almacenamiento persistente también pueden producirse errores como que el archivo ya existe, el directorio no existe o el espacio del disco se ha agotado. Los posibles casos de error que pueden producirse en un programa son muchos y variados.

En épocas más antiguas una forma de gestionar los errores era y sigue siendo con códigos de error donde la función o el código de salida de un programa retorna un 0 si no se ha producido ningún error o un número distinto de cero si se ha producido algún error, con un código de salida diferente por cada error. Dado que no hay obligación de gestionar adecuadamente el código de salida a veces no se hace con el consiguiente posible mal funcionamiento del programa. Otra forma de código de error es retornar un valor null en un método o función pero que no tratado adecuadamente producirá una excepción de tipo NullPointerException. Con la introducción de la clase Optional entre otras novedades de Java 8 los punteros nulos se gestionan más adecuadamente pero en los casos en los que se devuelve un puntero null no se proporciona información de cuál ha sido la condición de error que se ha producido.

Para obligar a gestionar adecuadamente las condiciones de error e informar de que posibles condiciones de error se pueden producir se incorporaron en algunos lenguajes las excepciones como en Java. Las excepciones checked, aquellas que son declaradas y de obligado tratamiento, garantizan que sean tratadas de alguna forma pero algo molestas con las sentencias try-catch-exception. Las excepciones unchecked, aquellas que no es necesario declararlas y no de obligado tratamiento, son arriesgadas ya que al igual que los códigos de error no obliga a darles un tratamiento además de que no se declaran que excepciones es posible que sean lanzadas.

 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package io.github.picodotdev.blogbitix.javaexception;

import io.vavr.control.Either;
import io.vavr.control.Try;

import java.util.Optional;
import java.util.concurrent.TransferQueue;

public class Main {

    private static int errorCode(boolean error) {
        return (error) ? 1 : 0;
    }

    private static Optional<Integer> optional(boolean error) {
        return (error) ? Optional.empty() : Optional.of(0);
    }

    private static Integer exception(boolean error) throws Exception {
        if (error) {
            throw new Exception();
        } else {
            return 0;
        }
    }

    private static Either<Exception, Integer> either(boolean error) {
        return (error) ? Either.left(new Exception()) : Either.right(1);
    }


    public static void main(String[] args) {
        int errorCode = errorCode(true);
        if (errorCode != 0) {
            System.out.printf("Error code: %d%n", errorCode);
        }

        Optional<Integer> value = optional(true);
        if (!value.isPresent()) {
            System.out.println("Optional empty");
        }

        try {
            exception(true);
        } catch (Exception e) {
            e.printStackTrace();
        }

        Either<Exception, Integer> either = either(true);
        if (either.isLeft()) {
            System.out.printf("Either exception: %s%n", either.getLeft().getClass().getName());
        }

        Try.<Integer>of(() -> exception(true)).onFailure(e -> System.out.printf("Try exception: %s%n", e.getClass().getName()));
    }
}
Main.java
1
2
3
4
5
6
7
Error code: 1
Optional empty
java.lang.Exception
        at io.github.picodotdev.blogbitix.javaexception.Main.exception(Main.java:21)
        at io.github.picodotdev.blogbitix.javaexception.Main.main(Main.java:44)
Either exception: java.lang.Exception
Try exception: java.lang.Exception
System.out

En algunos lenguajes con capacidades funcionales se ha propuesto una nueva forma para la gestión de condiciones de error, en Java y con la librería Vavr se proporciona la clase Either que es un tipo con la definición de tipo genérico Either<L,R>. Que un método devuelva Either<Exception, Integer> indica que puede devolver en el caso del ejemplo un Integer en el caso correcto o una excepción en el caso de error. Un potencial fallo de esta opción es que no hay obligación de usar un try-catch pero si se quiere usar el valor devuelto en caso correcto se ha de tener en cuenta el potencial caso de que lo haya es valor derecho. La clase Either proporciona métodos para tratar adecuadamente en caso de que esté presente el valor izquierdo o el valor derecho.

La clase Either tiene múltiples métodos para comprobar si el valor que tiene es un valor del tipo izquierdo, derecho, obtener el valor izquierdo, derecho y múltiples métodos que hereda de Value.

En vez de retornar un Either en un método usando Vavr se puede utilizar la clase Try como otra forma de gestionar las excepciones. Con Try el método no es necesario que devuelva un Either de modo que retorne el valor en el caso correcto y lance una excepción en caso de error. El Try puede convertirse a un Either.

Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradlew run


Comparte el artículo: