Статьи

Java 8 пятница: лучшие исключения

В Data Geekery мы любим Java. И так как мы действительно входим в свободный API jOOQ и запросы DSL , мы абсолютно взволнованы тем, что Java 8 принесет в нашу экосистему.

Ява 8 Пятница

Каждую пятницу мы показываем вам пару замечательных новых функций Java 8 в виде учебника, в которых используются лямбда-выражения, методы расширения и другие замечательные вещи. Вы найдете исходный код на GitHub .

Лучшие исключения

У меня возникла идея, когда я наткнулся на проблему # 706 JUnit GitHub , которая о предложении нового метода:

1
ExpectedException#expect(Throwable, Callable)

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

1
2
3
4
assertEquals(Exception.class,
    thrown(() -> foo()).getClass());
assertEquals("yikes!",
    thrown(() -> foo()).getMessage());

С другой стороны, почему бы просто не добавить что-то совершенно новое в соответствии с этим?

01
02
03
04
05
06
07
08
09
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
// This is needed to allow for throwing Throwables
// from lambda expressions
@FunctionalInterface
interface ThrowableRunnable {
    void run() throws Throwable;
}
 
// Assert a Throwable type
static void assertThrows(
    Class<? extends Throwable> throwable,
    ThrowableRunnable runnable
) {
    assertThrows(throwable, runnable, t -> {});
}
 
// Assert a Throwable type and implement more
// assertions in a consumer
static void assertThrows(
    Class<? extends Throwable> throwable,
    ThrowableRunnable runnable,
    Consumer<Throwable> exceptionConsumer
) {
    boolean fail = false;
    try {
        runnable.run();
        fail = true;
    }
    catch (Throwable t) {
        if (!throwable.isInstance(t))
            Assert.fail("Bad exception type");
 
        exceptionConsumer.accept(t);
    }
 
    if (fail)
        Assert.fail("No exception was thrown");
}

Таким образом, оба приведенных выше метода утверждают, что данный throwable генерируется из данного runnable — ThrowableRunnable , потому что большинство функциональных интерфейсов, к сожалению, не позволяют генерировать проверенные исключения. Смотрите эту статью для деталей .

Теперь мы используем приведенный выше гипотетический JUnit API как таковой:

1
2
3
4
5
6
assertThrows(Exception.class,
    () -> { throw new Exception(); });
 
assertThrows(Exception.class,
    () -> { throw new Exception("Message"); },
    e  -> assertEquals("Message", e.getMessage()));

На самом деле, мы могли бы даже пойти дальше и объявить исключительный вспомогательный метод, например:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
// This essentially swallows exceptions
static void withExceptions(
    ThrowableRunnable runnable
) {
    withExceptions(runnable, t -> {});
}
 
// This delegates exception handling to a consumer
static void withExceptions(
    ThrowableRunnable runnable,
    Consumer<Throwable> exceptionConsumer
) {
    try {
        runnable.run();
    }
    catch (Throwable t) {
        exceptionConsumer.accept(t);
    }
}

Это полезно, чтобы проглотить все виды исключений. Следующие две идиомы, таким образом, эквивалентны:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
try {
    // This will fail
    assertThrows(SQLException.class, () -> {
        throw new Exception();
    });
}
catch (Throwable t) {
    t.printStackTrace();
}
 
withExceptions(
    // This will fail
    () -> assertThrows(SQLException.class, () -> {
        throw new Exception();
    }),
    t -> t.printStackTrace()
);

Очевидно, что эти идиомы не обязательно более полезны, чем фактический блок try .. catch .. finally , особенно потому, что он не поддерживает правильную типизацию исключений (по крайней мере, в этом примере) и не поддерживает try-with заявление.

Тем не менее, такие полезные методы пригодятся время от времени.

Ссылка: Java 8, пятница: лучшие исключения от нашего партнера по JCG Лукаса Эдера в блоге JAVA, SQL и AND JOOQ .