Всегда приятно позаимствовать и украсть концепции и идеи из других языков. Опция 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 .