Всегда приятно позаимствовать и украсть концепции и идеи из других языков. Опция Scala — одна из идей, которая мне действительно нравится, поэтому я написал реализацию на Java. Он оборачивает объект, который может быть или не быть нулевым, и предоставляет некоторые методы для работы более функциональным способом. Например, метод isDefined добавляет объектно-ориентированный способ проверки, является ли значение нулевым. Затем он используется в других местах, таких как метод getOrElse, который в основном говорит: «Дайте мне то, что вы упаковываете, или запасной вариант, если он нулевой».
|
1
2
3
4
|
public T getOrElse(T fallback){ return isDefined() ? get() : fallback;} |
На практике это заменит традиционную Java, такую как
|
1
2
3
4
5
6
7
8
9
|
public void foo(){ String s = dao.getValue(); if (s == null) { s = 'bar'; } System.out.println(s);} |
с более лаконичным и оо
|
1
2
3
4
5
|
public void foo(){ Option<String> s = dao.getValue(); System.out.println(s.getOrElse('bar'));} |
Тем не менее, что, если я хочу сделать что-то другое, кроме получения запасного значения — скажем, выбросить исключение? Если говорить более конкретно, что если я захочу выдать конкретный тип исключения — то есть как специфического в использовании, так и не жестко закодированного в Option? Это требует хитрости и всплеска вывода типа.
Поскольку это Java, мы можем начать с новой фабрики — ExceptionFactory. Это базовая реализация, которая создает только исключения, созданные с помощью сообщения, но вы, конечно, можете расширить код по мере необходимости.
|
1
2
3
4
|
public interface ExceptionFactory <E extends Exception>{ E create(String message);} |
Обратите внимание, что <E расширяет исключение> — это ключ к тому, как это работает. Используя фабрику, теперь мы можем добавить новый метод в Option:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
public <E extends Exception> T getOrThrow(ExceptionFactory<E> exceptionFactory, String message) throws E{ if (isDefined()) { return get(); } else { throw exceptionFactory.create(message); }} |
Опять же, обратите внимание на броски E — это вывод из фабрики исключений.
И это, хотите верьте, хотите нет, это 90% того, что нужно. Единственное раздражение — необходимость иметь фабрики исключений. Если вы можете пережить это, у вас все готово. Давайте определим пару пользовательских исключений, чтобы увидеть это в действии.
|
01
02
03
04
05
06
07
08
09
10
11
12
|
public <E extends Exception> T getOrThrow(ExceptionFactory<E> exceptionFactory, String message) throws E{ if (isDefined()) { return get(); } else { throw exceptionFactory.create(message); }} |
И подозрительно похожее ExceptionB
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
public class ExceptionB extends Exception{ public ExceptionB(String message) { super(message); } public static ExceptionFactory<ExceptionB> factory() { return new ExceptionFactory<ExceptionB>() { @Override public ExceptionB create(String message) { return new ExceptionB(message); } }; }} |
И, наконец, бросить все это вместе:
|
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
|
public class GenericExceptionTest{ @Test(expected = ExceptionA.class) public void exceptionA_throw() throws ExceptionA { Option.option(null).getOrThrow(ExceptionA.factory(), "Some message pertinent to the situation"); } @Test public void exceptionA_noThrow() throws ExceptionA { String s = Option.option("foo").getOrThrow(ExceptionA.factory(), "Some message pertinent to the situation"); Assert.assertEquals("foo", s); } @Test(expected = ExceptionB.class) public void exceptionB_throw() throws ExceptionB { Option.option(null).getOrThrow(ExceptionB.factory(), "Some message pertinent to the situation"); } @Test public void exceptionB_noThrow() throws ExceptionB { String s = Option.option("foo").getOrThrow(ExceptionB.factory(), "Some message pertinent to the situation"); Assert.assertEquals("foo", s); }} |
Важно отметить, как выделено жирным шрифтом выше, исключение, объявленное в сигнатуре метода, является специфическим — оно не является общим предком (Exception или Throwable). Это означает, что теперь вы можете использовать параметры в своем слое DAO, слое обслуживания, где бы то ни было, и создавать конкретные исключения, где и как вам нужно.
Скачать исходный код : Вы можете получить исходный код и тесты здесь — genex
Примечание
Еще одна интересная вещь, появившаяся при написании этого, — наблюдение, что можно сделать это:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
public void foo(){ throw null;}public void bar(){ try { foo(); } catch (NullPointerException e) { ... }} |
Само собой разумеется, что это не очень хорошая идея.
Ссылка: Предполагаемые исключения в Java от нашего партнера JCG Стива Чалонера в блоге Objectify .