Я высоко ценю новые функции Java 8, касающиеся лямбд и интерфейсов методов по умолчанию. Тем не менее, я все еще скучаю с проверенными исключениями. Например, если я просто хочу перечислить все видимые поля объекта, я хотел бы просто написать это:
Arrays.asList(p.getClass().getFields()).forEach(
f -> System.out.println(f.get(p))
);
Тем не менее, поскольку get
метод может выдать проверенное исключение, которое не согласуется с Consumer
контрактом интерфейса, я должен перехватить это исключение и написать следующий код:
Arrays.asList(p.getClass().getFields()).forEach(
f -> {
try {
System.out.println(f.get(p));
} catch (IllegalArgumentException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
);
Однако в большинстве случаев я просто хочу, чтобы исключение было сгенерировано как a RuntimeException
и позволяло программе обрабатывать или нет исключение без ошибок компиляции.
Таким образом, я хотел бы иметь свое мнение о моем спорноге для обхода проверяемых исключений раздражения. Для этого я создал вспомогательный интерфейс ConsumerCheckException<T>
и служебную функцию rethrow
( обновленную в соответствии с предложением комментария Довала ) следующим образом:
@FunctionalInterface
public interface ConsumerCheckException<T>{
void accept(T elem) throws Exception;
}
public class Wrappers {
public static <T> Consumer<T> rethrow(ConsumerCheckException<T> c) {
return elem -> {
try {
c.accept(elem);
} catch (Exception ex) {
/**
* within sneakyThrow() we cast to the parameterized type T.
* In this case that type is RuntimeException.
* At runtime, however, the generic types have been erased, so
* that there is no T type anymore to cast to, so the cast
* disappears.
*/
Wrappers.<RuntimeException>sneakyThrow(ex);
}
};
}
/**
* Reinier Zwitserloot who, as far as I know, had the first mention of this
* technique in 2009 on the java posse mailing list.
* http://www.mail-archive.com/javaposse@googlegroups.com/msg05984.html
*/
public static <T extends Throwable> T sneakyThrow(Throwable t) {
throw (T) t;
}
}
И теперь я могу просто написать:
Arrays.asList(p.getClass().getFields()).forEach(
rethrow(f -> System.out.println(f.get(p)))
);
Я не уверен, что это лучшая идиома для обхода проверенных исключений, но, как я объяснил, я хотел бы иметь более удобный способ достижения моего первого примера, не имея дело с проверенными исключениями, и это более простой способ, который я нашел сделать это.
источник
sneakyThrow
внутри,rethrow
чтобы бросить исходное, проверенное исключение вместо того, чтобы обернуть его вRuntimeException
. В качестве альтернативы вы можете использовать@SneakyThrows
аннотацию от Project Lombok, которая делает то же самое.Consumer
s inforEach
может выполняться параллельно при использовании параллельныхStream
s. Затем отбрасываемое значение, вызванное отказом от потребителя, будет распространяться на вызывающий поток, который 1) не остановит других одновременно работающих потребителей, что может или не может быть уместно, и 2) если более одного из потребителей что-то выбрасывают, только одна из меток будет видна вызывающему потоку.Ответы:
Преимущества, недостатки и ограничения вашей техники:
Если вызывающий код должен обрабатывать проверенное исключение, вы ДОЛЖНЫ добавить его в предложение throws метода, который содержит поток. Компилятор больше не будет заставлять вас добавлять его, поэтому его легче забыть. Например:
Если вызывающий код уже обрабатывает проверенное исключение, компилятор напомнит вам добавить предложение throws к объявлению метода, содержащему поток (если вы этого не сделаете, он скажет: исключение никогда не выдается в теле соответствующего оператора try) ,
В любом случае, вы не сможете окружить сам поток, чтобы поймать проверенное исключение ВНУТРИ метода, который содержит поток (если вы попытаетесь, компилятор скажет: исключение никогда не выдается в теле соответствующего оператора try).
Если вы вызываете метод, который буквально никогда не может выбросить исключение, которое он объявляет, вам не следует включать предложение throws. Например: new String (byteArr, "UTF-8") выбрасывает UnsupportedEncodingException, но UTF-8 гарантируется, что спецификация Java всегда присутствует. Здесь, декларация бросков - неприятность, и любое решение заставить ее замолчать с минимальным количеством шаблонов приветствуется.
Если вы ненавидите проверенные исключения и чувствуете, что они никогда не должны добавляться в язык Java с самого начала (все больше людей думают так, а я НЕ один из них), тогда просто не добавляйте проверенное исключение к броскам предложение метода, содержащего поток. Тогда проверенное исключение будет вести себя так же, как исключение UNchecked.
Если вы реализуете строгий интерфейс, в котором у вас нет возможности добавить объявление throws, и, тем не менее, выбрасывать исключение вполне уместно, тогда оборачивание исключения только для того, чтобы получить привилегию для его выброса, приводит к трассировке стека с ложными исключениями, которые не сообщать информацию о том, что на самом деле пошло не так. Хорошим примером является Runnable.run (), который не генерирует никаких проверенных исключений. В этом случае вы можете решить не добавлять проверенное исключение в предложение throws метода, содержащего поток.
В любом случае, если вы решите НЕ добавлять (или забыть добавить) проверенное исключение к предложению throws метода, содержащего поток, учтите следующие 2 последствия выброса исключений CHECKED:
Вызывающий код не сможет поймать его по имени (если вы попробуете, компилятор скажет: исключение никогда не выдается в теле соответствующего оператора try). Он будет пузыриться и, вероятно, будет перехвачен в цикле основной программы каким-нибудь «catch Exception» или «catch Throwable», что может быть тем, что вы хотите в любом случае.
Это нарушает принцип наименьшего удивления: вам больше не будет достаточно перехватить RuntimeException, чтобы гарантировать возможность перехвата всех возможных исключений. По этой причине я считаю, что это следует делать не в программном коде, а только в бизнес-коде, который вы полностью контролируете.
Рекомендации:
ПРИМЕЧАНИЕ. Если вы решите использовать этот метод, вы можете скопировать
LambdaExceptionUtil
вспомогательный класс из StackOverflow: https://stackoverflow.com/questions/27644361/how-can-i-throw-checked-exceptions-from-inside-java-8 -потоки . Это дает вам полную реализацию (Function, Consumer, Supplier ...), с примерами.источник
В этом примере, может ли он когда-нибудь действительно потерпеть неудачу? Не думаю, но, возможно, ваш случай особенный. Если он действительно «не может потерпеть неудачу», и это просто раздражает компилятор, я хотел бы обернуть исключение и
Error
добавить комментарий «не может произойти». Делает вещи очень понятными для обслуживания. Иначе они будут удивляться, «как это может случиться» и «кто, черт возьми, справляется с этим?»Это в спорной практике так YMMV. Я, вероятно, получу некоторые отрицательные отзывы.
источник
clone
запечатанный тип,Foo
который, как известно, поддерживает его, и он выбрасываетCloneNotSupportedException
. Как это могло произойти, если код не был связан с каким-то другим неожиданным видомFoo
? И если это произойдет, можно ли доверять чему-либо?An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch.
(цитата из Javadoc ofError
). Это как раз такая ситуация, поэтому она далеко не является неадекватной, поэтому использованиеError
здесь является наиболее подходящим вариантом.другая версия этого кода, где проверенное исключение просто задерживается:
магия в том, что если вы позвоните:
тогда ваш код будет обязан перехватить исключение
источник
подлый это круто! Я полностью чувствую твою боль.
Если вам нужно остаться на Яве ...
Paguro имеет функциональные интерфейсы, которые обертывают проверенные исключения, поэтому вам больше не нужно об этом думать. Он включает в себя неизменяемые коллекции и функциональные преобразования по типу преобразователей Clojure (или потоков Java 8), которые принимают функциональные интерфейсы и переносят проверенные исключения.
Также можно попробовать VAVr и The Eclipse Collections .
В противном случае используйте Kotlin
Kotlin является двухсторонней совместимостью с Java, не имеет проверенных исключений, никаких примитивов (ну, не о чем должен думать программист), лучшей системы типов, предполагает неизменяемость и т. Д. Несмотря на то, что я курирую библиотеку Paguro выше, я ' Я преобразую весь код Java, который я могу, в Kotlin. Все, что я делал на Яве, теперь я предпочитаю делать на Котлине.
источник
Проверенные исключения очень полезны, и их обработка зависит от типа продукта, частью которого вы код:
Обычно библиотека не должна обрабатывать проверенные исключения, а объявляется как генерируемая публичным API. Редкий случай, когда они могут быть обернуты в обычный RuntimeExcepton, например, создает внутри кода библиотеки некоторый частный XML-документ и анализирует его, создает некоторый временный файл и затем читает его.
Для настольных приложений вы должны начать разработку продукта, создав собственную структуру обработки исключений. Используя свою собственную инфраструктуру, обычно вы оборачиваете проверенное исключение в некоторые из двух-четырех упаковщиков (ваши пользовательские подклассы RuntimeExcepion) и обрабатывает их одним обработчиком исключений.
Для серверов, как правило, ваш код будет работать внутри какой-то платформы. Если вы не используете какой-либо (пустой сервер), создайте свой собственный фреймворк, аналогичный (на основе Runtime), описанный выше.
Для академических упражнений вы всегда можете обернуть их непосредственно в RuntimeExcepton. Кто-то может создать крошечный каркас тоже.
источник
Возможно, вы захотите взглянуть на этот проект ; Я создал его специально из-за проблемы проверенных исключений и функциональных интерфейсов.
С его помощью вы можете сделать это вместо написания собственного кода:
Поскольку все эти интерфейсы Throwing * расширяют свои аналоги, не относящиеся к Throwing, вы можете использовать их непосредственно в потоке:
Или вы можете просто:
И другие вещи.
источник
fields.forEach((ThrowingConsumer<Field>) f -> out.println(f.get(o)))