Рубрики
Без рубрики

Введение без исключений

Узнайте о библиотеке без исключений, которая предоставляет краткие и удобные обработчики исключений.

Автор оригинала: baeldung.

1. Обзор

Иногда блоки try/catch могут привести к многословным или даже неудобным конструкциям кода.

В этой статье мы сосредоточимся на No Exception , который предоставляет краткие и удобные обработчики исключений.

2. Зависимость Maven

Давайте добавим Без исключений к вашему pom.xml :


    com.machinezoo.noexception
    noexception
    1.1.0

3. Стандартная Обработка Исключений

Давайте начнем с общепринятой идиомы:

private static Logger logger = LoggerFactory.getLogger(NoExceptionUnitTest.class);

@Test
public void whenStdExceptionHandling_thenCatchAndLog() {
    try {
        logger.info("Result is " + Integer.parseInt("foobar"));
    } catch (Throwable exception) {
        logger.error("Caught exception:", exception);
    }
}

Мы начинаем с выделения регистратора , а затем вводим try блок. Если возникает Исключение , мы регистрируем его:

09:29:28.140 [main] ERROR c.b.n.NoExceptionUnitTest 
  - Caught exception
j.l.NumberFormatException: For input string: "foobar"
at j.l.NumberFormatException.forInputString(NumberFormatException.java:65)
at j.l.Integer.parseInt(Integer.java:580)
...

4. Обработка Исключений Без Исключений

4.1. Обработчик ведения журнала по умолчанию

Давайте заменим это на No Exception стандартный обработчик исключений:

@Test
public void whenDefaultNoException_thenCatchAndLog() {
    Exceptions 
      .log()
      .run(() -> System.out.println("Result is " + Integer.parseInt("foobar")));
}

Этот код дает нам почти тот же результат, что и выше:

09:36:04.461 [main] ERROR c.m.n.Exceptions 
  - Caught exception
j.l.NumberFormatException: For input string: "foobar"
at j.l.NumberFormatException.forInputString(NumberFormatException.java:65)
at j.l.Integer.parseInt(Integer.java:580)
...

В своей самой основной форме No Exception предоставляет нам способ заменить try/catch исключения одной строкой кода. Он выполняет лямбду , которую мы передаем в run () , и если возникает Исключение , оно регистрируется.

4.2. Добавление пользовательского регистратора

Если мы внимательно посмотрим на выходные данные, мы увидим, что исключения регистрируются как класс ведения журнала, а не наш.

Мы можем это исправить, предоставив наш регистратор:

@Test
public void whenDefaultNoException_thenCatchAndLogWithClassName() {
    Exceptions
      .log(logger)
      .run(() -> System.out.println("Result is " + Integer.parseInt("foobar")));
}

Что дает нам этот результат:

09:55:23.724 [main] ERROR c.b.n.NoExceptionUnitTest 
  - Caught exception
j.l.NumberFormatException: For input string: "foobar"
at j.l.NumberFormatException.forInputString(NumberFormatException.java:65)
at j.l.Integer.parseInt(Integer.java:580)
...

4.3. Предоставление пользовательского сообщения журнала

Возможно, мы захотим использовать другое сообщение, чем “Пойманное исключение по умолчанию”.” Мы можем сделать это, передав Регистратор в качестве первого аргумента и Строку сообщение в качестве второго :

@Test
public void whenDefaultNoException_thenCatchAndLogWithMessage() {
    Exceptions
      .log(logger, "Something went wrong:")
      .run(() -> System.out.println("Result is " + Integer.parseInt("foobar")));
}

Что дает нам этот результат:

09:55:23.724 [main] ERROR c.b.n.NoExceptionUnitTest 
  - Something went wrong:
j.l.NumberFormatException: For input string: "foobar"
at j.l.NumberFormatException.forInputString(NumberFormatException.java:65)
at j.l.Integer.parseInt(Integer.java:580)
...

Но что, если мы хотим сделать больше , чем просто регистрировать Исключения , например, вставить запасное значение, когда parseInt() завершается неудачей?

4.4. Указание значения по умолчанию

Исключения могут возвращать результат, завернутый в Необязательный . Давайте переместим вещи, чтобы мы могли использовать его для предоставления значения по умолчанию, если цель потерпит неудачу:

@Test
public void
  givenDefaultValue_whenDefaultNoException_thenCatchAndLogPrintDefault() {
    System.out.println("Result is " + Exceptions
      .log(logger, "Something went wrong:")
      .get(() -> Integer.parseInt("foobar"))
      .orElse(-1));
}

Мы все еще видим наше Исключение :

12:02:26.388 [main] ERROR c.b.n.NoExceptionUnitTest
  - Caught exception java.lang.NumberFormatException: For input string: "foobar"
at j.l.NumberFormatException.forInputString(NumberFormatException.java:65)
at j.l.Integer.parseInt(Integer.java:580)
...

Но мы также видим наше сообщение, напечатанное на консоли:

Result is -1

5. Создание пользовательского обработчика ведения журнала

До сих пор у нас есть хороший метод, позволяющий избежать повторения и сделать код более читаемым в простых сценариях try/catch/log . Что делать, если мы хотим повторно использовать обработчик с другим поведением?

Давайте расширим No Exception ‘s Обработчик исключений класс и выполним одну из двух вещей в зависимости от типа исключения:

public class CustomExceptionHandler extends ExceptionHandler {

Logger logger = LoggerFactory.getLogger(CustomExceptionHandler.class);

    @Override
    public boolean handle(Throwable throwable) {
        if (throwable.getClass().isAssignableFrom(RuntimeException.class)
          || throwable.getClass().isAssignableFrom(Error.class)) {
            return false;
        } else {
            logger.error("Caught Exception", throwable);
            return true;
        }
    }
}

Возвращаясь ложный когда мы видим Ошибка или Исключение RuntimeException мы рассказываем Обработчик исключений чтобы снова бросить. Возвращаясь истинный для всего остального мы указываем, что исключение было обработано.

Во-первых, мы запустим это со стандартным исключением:

@Test
public void givenCustomHandler_whenError_thenRethrowError() {
    CustomExceptionHandler customExceptionHandler = new CustomExceptionHandler();
    customExceptionHandler.run(() -> "foo".charAt(5));
}

Мы передаем нашу функцию методу run() в нашем пользовательском обработчике, унаследованном от обработчика исключений :

18:35:26.374 [main] ERROR c.b.n.CustomExceptionHandler 
  - Caught Exception 
j.l.StringIndexOutOfBoundsException: String index out of range: 5
at j.l.String.charAt(String.java:658)
at c.b.n.CustomExceptionHandling.throwSomething(CustomExceptionHandling.java:20)
at c.b.n.CustomExceptionHandling.lambda$main$0(CustomExceptionHandling.java:10)
at c.m.n.ExceptionHandler.run(ExceptionHandler.java:1474)
at c.b.n.CustomExceptionHandling.main(CustomExceptionHandling.java:10)

Это исключение регистрируется. Давайте попробуем с Ошибкой :

@Test(expected = Error.class)
public void givenCustomHandler_whenException_thenCatchAndLog() {
    CustomExceptionHandler customExceptionHandler = new CustomExceptionHandler();
    customExceptionHandler.run(() -> throwError());
}

private static void throwError() {
    throw new Error("This is very bad.");
}

И мы видим , что Ошибка была повторно брошена в main () , а не зарегистрирована:

Exception in thread "main" java.lang.Error: This is very bad.
at c.b.n.CustomExceptionHandling.throwSomething(CustomExceptionHandling.java:15)
at c.b.n.CustomExceptionHandling.lambda$main$0(CustomExceptionHandling.java:8)
at c.m.n.ExceptionHandler.run(ExceptionHandler.java:1474)
t c.b.n.CustomExceptionHandling.main(CustomExceptionHandling.java:8)

Таким образом, у нас есть многоразовый класс, который может использоваться во всем проекте для последовательной обработки исключений.

6. Заключение

С Без исключений мы можем упростить обработку исключений в каждом конкретном случае с помощью одной строки кода.

Код можно найти в этом проекте GitHub .