Cómo hacer excepciones personalizadas en Java

Visión general

En este artículo, cubriremos el proceso de creación de excepciones tanto seleccionadas como no seleccionadas en Java.
Si desea leer más sobre las excepciones y el manejo de excepciones en Java, lo hemos cubierto en detalle en -Manejo de excepciones en Java: Una guía completa con las mejores y las peores prácticas.

¿Por qué usar excepciones personalizadas?

Aunque las excepciones de Java, tal como son, cubren casi todos los casos y condiciones excepcionales, su aplicación puede lanzar una excepción personalizada específica, única para su código y lógica.
A veces, necesitamos crear el nuestro para representar excepciones de lógica de negocios, es decir, excepciones que son específicas de nuestra lógica de negocios o flujo de trabajo. Por ejemploEmailNotUniqueException,InvalidUserStateException, etc.
Ayudan a los clientes de aplicaciones a comprender mejor lo que salió mal. Son particularmente útiles para realizar el manejo de excepciones para las API REST, ya que diferentes restricciones de lógica de negocios requieren diferentes códigos de respuesta para ser devueltos al cliente.
Si la definición de una excepción personalizada no proporciona beneficios sobre el uso de una excepción regular en Java, no hay necesidad de definir excepciones personalizadas y debe limitarse a las que ya están disponibles, no es necesario inventar el agua caliente nuevamente.

Excepción comprobada personalizada

Consideremos un escenario en el que queremos validar un correo electrónico que se pasa como un argumento a un método.
Queremos comprobar si es válido o no. Ahora podríamos usar el Java incorporadoIllegalArgumentException, lo cual está bien si solo estamos comprobando una sola cosa, como si coincide con un REGEX predefinido o no.
Pero supongamos que también tenemos una condición comercial para verificar que todos los correos electrónicos en nuestro sistema deben ser únicos. Ahora tenemos que realizar una segunda verificación (DB / llamada de red). Podemos, por supuesto, usar el mismoIllegalArgumentException, pero no quedará claro cuál es la causa exacta, ya sea que la validación REGEX del correo electrónico no sea correcta o el correo electrónico ya exista en la base de datos.
Vamos a crear una excepción personalizada para manejar esta situación. Para crear una excepción, como cualquier otra excepción, tenemos que extender lajava.lang.Exception clase:
public class EmailNotUniqueException extends Exception {

    public EmailNotUniqueException(String message) {
        super(message);
    }
}
Tenga en cuenta que proporcionamos un constructor que toma un String mensaje de error y llama al constructor de la clase principal. Ahora, esto no es obligatorio pero es una práctica común tener algún tipo de detalles sobre la excepción que ocurrió.
Al llamar super(message), inicializamos el mensaje de error de la excepción y la clase base se encarga de configurar el mensaje personalizado, de acuerdo con elmessage.
Ahora, usemos esta excepción personalizada en nuestro código. Como estamos definiendo un método que puede generar una excepción en la capa de servicio, lo marcaremos con la throws palabra clave.
Si el correo electrónico de entrada ya existe en nuestra base de datos (en este caso, una lista), tenemos thrownuestra excepción personalizada:
public class RegistrationService {  
    List<String> registeredEmails = Arrays.asList("abc@gmail.com", "xyz@gmail.com");

    public void validateEmail(String email) throws EmailNotUniqueException {
        if (registeredEmails.contains(email)) {
            throw new EmailNotUniqueException("Email Already Registered");
        }
    }
}
Ahora escribamos un cliente para nuestro servicio. Dado que es una excepción marcada, debemos cumplir con la regla de manejar o declarar. En el ejemplo que sigue, decidimos "manejarlo":
public class RegistrationServiceClient {  
    public static void main(String[] args) {
        RegistrationService service = new RegistrationService();
        try {
            service.validateEmail("abc@gmail.com");
        } catch (EmailNotUniqueException e) {
            // logging and handling the situation
        }
    }
}
Ejecutar este fragmento de código dará como resultado:
mynotes.custom.checked.exception.EmailNotUniqueException: Email Already Registered  
    at mynotes.custom.checked.exception.RegistrationService.validateEmail(RegistrationService.java:12)
    at mynotes.custom.checked.exception.RegistrationServiceClient.main(RegistrationServiceClient.java:9)
Nota: el proceso de manejo de excepciones se omite por brevedad, pero aún así es un proceso importante.

Excepción personalizada sin marcar

Esto funciona perfectamente bien, pero nuestro código se ha vuelto un poco desordenado. Además, estamos obligando al cliente a capturar nuestra excepción en untry-catch bloque. En algunos casos, esto puede llevar a los desarrolladores a escribir código repetitivo.
En este caso, puede ser beneficioso crear una excepción de tiempo de ejecución personalizada. Para crear una excepción personalizada sin marcar, tenemos que ampliar la java.lang.RuntimeException clase.
Consideremos la situación en la que tenemos que comprobar si el correo electrónico tiene un nombre de dominio válido o no:
public class DomainNotValidException extends RuntimeException {

    public DomainNotValidException(String message) {
        super(message);
    }
}
Ahora déjalo usar en nuestro servicio:
public class RegistrationService {

    public void validateEmail(String email) {
        if (!isDomainValid(email)) {
            throw new DomainNotValidException("Invalid domain");
        }
    }

    private boolean isDomainValid(String email) {
        List<String> validDomains = Arrays.asList("gmail.com", "yahoo.com", "outlook.com");
        if (validDomains.contains(email.substring(email.indexOf("@") + 1))) {
            return true;
        }
        return false;
    }
}
Tenga en cuenta que no tuvimos que usar las throwspalabras clave en la firma del método, ya que se trata de una excepción sin marcar.
Ahora escribamos un cliente para nuestro servicio. No tenemos que usar un try-catch bloque esta vez:
public class RegistrationServiceClient {

    public static void main(String[] args) {
        RegistrationService service = new RegistrationService();
        service.validateEmail("abc@gmail1.com");
    }
}
Ejecutar este fragmento de código dará como resultado:
Exception in thread "main" mynotes.custom.unchecked.exception.DomainNotValidException: Invalid domain  
    at mynotes.custom.unchecked.exception.RegistrationService.validateEmail(RegistrationService.java:10)
    at mynotes.custom.unchecked.exception.RegistrationServiceClient.main(RegistrationServiceClient.java:7)
Nota: Por supuesto, puede rodear su código con untry-catch bloque para capturar la excepción que surge, pero ahora el compilador no la obliga.

Volver a generar una excepción envuelta dentro de una excepción personalizada

A veces necesitamos capturar una excepción y volver a lanzarla agregando algunos detalles más. Esto suele ser común si tiene varios códigos de error definidos en su aplicación que necesitan ser registrados o devueltos al cliente en el caso de esa excepción en particular.
Supongamos que su aplicación tiene una ErrorCodesclase estándar :
public enum ErrorCodes {  
    VALIDATION_PARSE_ERROR(422);

    private int code;

    ErrorCodes(int code) {
        this.code = code;
    }

    public int getCode() {
        return code;
    }
}
Vamos a crear nuestra excepción personalizada:
public class InvalidCurrencyDataException extends RuntimeException {

    private Integer errorCode;

    public InvalidCurrencyDataException(String message) {
        super(message);
    }

    public InvalidCurrencyDataException(String message, Throwable cause) {
        super(message, cause);
    }

    public InvalidCurrencyDataException(String message, Throwable cause, ErrorCodes errorCode) {
        super(message, cause);
        this.errorCode = errorCode.getCode();
    }

    public Integer getErrorCode() {
        return errorCode;
    }
}
Observe que tenemos varios constructores y deje que la clase de servicio decida cuál usar. Dado que estamos volviendo a generar la excepción, siempre es una buena práctica capturar la causa raíz de la excepción, de ahí elThrowable argumento que se puede pasar al constructor de la clase principal.
También estamos capturando el código de error en uno de los constructores y configuramos el mismoerrorCode dentro de la excepción. El errorCode cliente podría utilizarlo para el registro o cualquier otro propósito. Esto ayuda en un estándar más centralizado para el manejo de excepciones.
Vamos a escribir nuestra clase de servicio:
public class RegistrationService {  
    List<String> registeredEmails = Arrays.asList("abc@gmail.com", "xyz@gmail.com");

    public void validateEmail(String email) throws EmailNotUniqueException {
        if (registeredEmails.contains(email)) {
            throw new EmailNotUniqueException("Email Already Registered");
        }
    }
}
Así que cogimos el estandarte NumberFormatExceptiony lanzamos el nuestro InvalidCurrencyDataExceptionPasamos la excepción principal a nuestra excepción personalizada para que no perdamos la causa raíz por la que ocurrieron.
Vamos a escribir un cliente de prueba para este servicio:
public class CurrencyClient {  
    public static void main(String[] args) {
        CurrencyService service = new CurrencyService();
    service.convertDollarsToEuros("asd");
    }
}
Salida:
Exception in thread "main" mynotes.custom.unchecked.exception.InvalidCurrencyDataException: Invalid data  
    at mynotes.custom.unchecked.exception.CurrencyService.convertDollarsToEuro(CurrencyService.java:10)
    at mynotes.custom.unchecked.exception.CurrencyClient.main(CurrencyClient.java:8)
Caused by: java.lang.NumberFormatException: For input string: "asd"  
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:580)
    at java.lang.Integer.parseInt(Integer.java:615)
    at mynotes.custom.unchecked.exception.CurrencyService.convertDollarsToEuro(CurrencyService.java:8)
    ... 1 more
Como puede ver, tenemos un buen seguimiento de pila de la excepción que podría ser útil para propósitos de depuración.

Mejores prácticas para excepciones personalizadas

  • Cumpla con la convención general de nomenclatura en todo el ecosistema de Java: todos los nombres de clase de excepción personalizados deben terminar con "Excepción"
  • Evite hacer excepciones personalizadas si las excepciones estándar del propio JDK pueden cumplir su función. En la mayoría de los casos, no es necesario definir excepciones personalizadas.
  • Prefiere las excepciones de tiempo de ejecución sobre las excepciones verificadas. Los marcos como Spring han envuelto todas las excepciones verificadas a las excepciones de tiempo de ejecución, por lo que no obligan al cliente a escribir el código de la placa que no quieren o no necesitan.
  • Proporcione muchos constructores sobrecargados en función de cómo se lanzará la excepción personalizada. Si se está utilizando para volver a generar una excepción existente, proporcione definitivamente un constructor que establezca la causa.

Conclusión

Se utilizan excepciones personalizadas para requisitos y lógica de negocios específicos. En este artículo, hemos discutido la necesidad de ellos y hemos cubierto su uso.
El código para los ejemplos utilizados en este artículo se puede encontrar en Github .

Acerca de: Programator

Somos Instinto Programador

0 comentarios:

Publicar un comentario

Dejanos tu comentario para seguir mejorando!

Con tecnología de Blogger.