У меня возникла проблема при проверке лямбда-выражений Java 8. Обычно это работает нормально, но теперь у меня есть методы, которые бросают IOException
. Лучше всего, если вы посмотрите на следующий код:
class Bank{
....
public Set<String> getActiveAccountNumbers() throws IOException {
Stream<Account> s = accounts.values().stream();
s = s.filter(a -> a.isActive());
Stream<String> ss = s.map(a -> a.getNumber());
return ss.collect(Collectors.toSet());
}
....
}
interface Account{
....
boolean isActive() throws IOException;
String getNumber() throws IOException;
....
}
Проблема в том, что он не компилируется, потому что я должен перехватить возможные исключения из isActive- и getNumber-Methods. Но даже если я явно использую блок try-catch-Block, как показано ниже, он все равно не скомпилируется, потому что я не улавливаю исключение. Так что либо есть ошибка в JDK, либо я не знаю, как перехватить эти исключения.
class Bank{
....
//Doesn't compile either
public Set<String> getActiveAccountNumbers() throws IOException {
try{
Stream<Account> s = accounts.values().stream();
s = s.filter(a -> a.isActive());
Stream<String> ss = s.map(a -> a.getNumber());
return ss.collect(Collectors.toSet());
}catch(IOException ex){
}
}
....
}
Как я могу заставить это работать? Может кто-нибудь подсказать мне правильное решение?
java
exception-handling
lambda
java-8
Мартин Вебер
источник
источник
Ответы:
Вы должны поймать исключение, прежде чем оно выйдет из лямбды:
Примите во внимание тот факт, что лямбда не оценивается в том месте, где вы ее пишете, а в каком-то совершенно не связанном месте, в классе JDK. Так что это будет точка, где будет выброшено это проверенное исключение, и в этом месте оно не объявлено.
Вы можете справиться с этим, используя оболочку вашей лямбды, которая переводит проверенные исключения в непроверенные:
Ваш пример будет написан как
В своих проектах я занимаюсь этим вопросом без обертывания; вместо этого я использую метод, который эффективно предотвращает проверку исключений компилятором. Излишне говорить, что с этим следует обращаться осторожно, и все участники проекта должны знать, что проверенное исключение может появиться там, где оно не объявлено. Это код слесарного дела:
и вы можете ожидать получить
IOException
бросок в лицо, даже еслиcollect
не объявить об этом. В большинстве случаев , но не во всех реальных, вы все равно захотите просто сбросить исключение и обработать его как общий сбой. Во всех этих случаях ничего не теряется в ясности или правильности. Просто остерегайтесь тех других случаев, когда вы действительно захотите отреагировать на исключение на месте. Разработчик не будет предупрежден компилятором о том, что егоIOException
нужно перехватить, и компилятор на самом деле будет жаловаться, если вы попытаетесь его перехватить, потому что мы обманули его, полагая, что такое исключение не может быть сгенерировано.источник
Вы также можете распространять свою статическую боль с помощью лямбд, так что все это выглядит читабельно:
propagate
здесь получаетjava.util.concurrent.Callable
в качестве параметра и преобразует любое исключение, пойманное во время вызоваRuntimeException
. В Гуаве есть похожий метод преобразования Throwables # пропагандировать (Throwable) .Этот метод, по-видимому, необходим для цепочки лямбда-методов, поэтому я надеюсь, что однажды он будет добавлен к одной из популярных библиотек, или это распространяющееся поведение будет по умолчанию.
источник
Этот
UtilException
вспомогательный класс позволяет вам использовать любые проверенные исключения в потоках Java, например:Примечание
Class::forName
кидаетClassNotFoundException
, что проверено . Сам поток также выдаетClassNotFoundException
, а НЕ какое-то неконтролируемое исключение обтекания.Много других примеров того, как его использовать (после статического импорта
UtilException
):Но не используйте его до понимания следующих преимуществ, недостатков и ограничений :
• Если вызывающий код должен обрабатывать проверенное исключение, вы ДОЛЖНЫ добавить его в предложение throws метода, который содержит поток. Компилятор больше не будет заставлять вас добавлять его, поэтому его легче забыть.
• Если вызывающий код уже обрабатывает проверенное исключение, компилятор напомнит вам добавить предложение throws к объявлению метода, содержащему поток (если вы этого не сделаете, он скажет: исключение никогда не выдается в теле соответствующего оператора try ).
• В любом случае вы не сможете окружить сам поток, чтобы поймать проверенное исключение ВНУТРИ метода, который содержит этот поток (если вы попытаетесь, компилятор скажет: исключение никогда не выдается в теле соответствующего оператора try).
• Если вы вызываете метод, который буквально никогда не может выбросить исключение, которое он объявляет, вам не следует включать предложение throws. Например: new String (byteArr, "UTF-8") выбрасывает UnsupportedEncodingException, но UTF-8 гарантируется, что спецификация Java всегда присутствует. Здесь, декларация бросков - неприятность, и любое решение заставить ее замолчать с минимальным шаблоном приветствуется.
• Если вы ненавидите проверенные исключения и чувствуете, что их никогда не следует добавлять в язык Java для начала (все больше людей думают так, а я НЕ один из них), то просто не добавляйте проверенное исключение в бросает предложение метода, который содержит поток. Таким образом, проверенное исключение будет вести себя так же, как исключение UNchecked.
• Если вы реализуете строгий интерфейс, в котором у вас нет возможности добавить объявление throws, и, тем не менее, выбрасывать исключение вполне уместно, тогда обертывание исключения только для того, чтобы получить привилегию для его выброса, приводит к трассировке стека с ложными исключениями которые не дают никакой информации о том, что на самом деле пошло не так. Хорошим примером является Runnable.run (), который не выдает никаких проверенных исключений. В этом случае вы можете решить не добавлять проверенное исключение в предложение throws метода, содержащего поток.
• В любом случае, если вы решите НЕ добавлять (или забыть добавить) проверенное исключение к предложению throws метода, содержащего поток, учтите следующие 2 последствия выброса исключений CHECKED:
1) вызывающий код не сможет поймать его по имени (если вы попытаетесь, компилятор скажет: исключение никогда не выдается в теле соответствующего оператора try). Он будет пузыриться и, вероятно, будет перехвачен в цикле основной программы каким-то «catch Exception» или «catch Throwable», что может быть тем, что вы хотите в любом случае.
2) Это нарушает принцип наименьшего удивления: вам больше не будет достаточно перехватить RuntimeException, чтобы иметь возможность гарантировать перехват всех возможных исключений. По этой причине я считаю, что это следует делать не в программном коде, а только в бизнес-коде, который вы полностью контролируете.
В заключение: я считаю, что ограничения здесь несерьезны, и
UtilException
класс можно использовать без страха. Тем не менее, это зависит от вас!источник
Вы можете потенциально свернуть свой собственный
Stream
вариант, обернув лямбду, чтобы создать исключение без проверки, а затем развернуть это исключение при операциях терминала:Тогда вы можете использовать это точно так же, как
Stream
:Это решение потребует довольно много шаблонного, поэтому я предлагаю вам взглянуть на библиотеку, которую я уже создал, которая в точности соответствует тому, что я описал здесь для всего
Stream
класса (и даже больше!).источник
new StreamBridge<>(ts, IOException.class);
->new StreamBridge<>(s, IOException.class);
StreamAdapter
.Используйте метод #propagate (). Пример не-гуавской реализации из блога Java 8 Сэма Берана :
источник
Это не дает прямого ответа на вопрос (есть много других ответов), но в первую очередь пытается избежать проблемы:
По моему опыту, необходимость обрабатывать исключения в
Stream
(или другом лямбда-выражении) часто вытекает из того факта, что исключения объявляются как методы, в которых они не должны создаваться. Это часто происходит от смешивания бизнес-логики с входом и выходом. ВашAccount
интерфейс является прекрасным примером:Вместо того, чтобы бросать
IOException
на каждый получатель, рассмотрите этот дизайн:Метод
AccountReader.readAccount(…)
может считывать учетную запись из базы данных, файла или чего-либо еще и выдавать исключение, если это не удается. Он создаетAccount
объект, который уже содержит все значения, готовые к использованию. Поскольку значения уже были загруженыreadAccount(…)
, получатели не будут генерировать исключение. Таким образом, вы можете свободно использовать их в лямбдах без необходимости оборачивать, маскировать или скрывать исключения.Конечно, это не всегда возможно сделать так, как я описал, но часто это так и приводит к более чистому коду (ИМХО):
throws IOException
ни для чего, кроме как для удовлетворения компилятораAccount
неизменным и извлечь выгоду из его преимуществ (например, безопасность потока)Account
в лямбдах (например, вStream
)источник
Это можно решить с помощью простого кода ниже с Stream и Try в AbacusUtil :
Раскрытие информации: я разработчик
AbacusUtil
.источник
Расширяя решение @marcg, вы обычно можете бросить и поймать проверенное исключение в Streams; то есть компилятор попросит вас перехватить / перебросить, как если бы вы были вне потоков!
Тогда ваш пример будет записан следующим образом (добавление тестов, чтобы показать это более четко):
источник
Чтобы правильно добавить код обработки IOException (в RuntimeException), ваш метод будет выглядеть следующим образом:
Проблема в том, что
IOException
нужно будет записать какRuntimeException
и преобразовать обратно вIOException
- и это добавит еще больше кода к вышеуказанному методу.Зачем использовать,
Stream
когда это можно сделать именно так - и метод выбрасывает,IOException
поэтому для этого тоже не требуется никакого дополнительного кода:источник
Помня об этой проблеме, я разработал небольшую библиотеку для работы с проверенными исключениями и лямбдами. Пользовательские адаптеры позволяют интегрироваться с существующими функциональными типами:
https://github.com/TouK/ThrowingFunction/
источник
Ваш пример может быть записан как:
Класс Unthrow можно взять здесь https://github.com/SeregaLBN/StreamUnthrower
источник
Если вы не возражаете против использования сторонних библиотек, в библиотеке AOL cyclops-реагировать , disclosure :: I contributor есть класс ExceptionSoftener, который может помочь здесь.
источник
Функциональные интерфейсы в Java не объявляют никаких проверенных или непроверенных исключений. Нам нужно изменить сигнатуру методов с:
Для того, чтобы:
Или обработайте его с помощью блока try-catch:
Другой вариант - написать пользовательскую оболочку или использовать библиотеку, например ThrowingFunction. С библиотекой нам нужно только добавить зависимость в наш pom.xml:
И использовать определенные классы, такие как ThrowingFunction, ThrowingConsumer, ThrowingPredicate, ThrowingRunnable, ThrowingSupplier.
В конце код выглядит так:
источник
Я не вижу какого-либо способа обработки проверенного исключения в потоке (Java-8), единственный способ, которым я применил, - это перехватить проверенное исключение в потоке и повторно выбросить их как непроверенное исключение.
источник
Если вы хотите обработать исключение в потоке и продолжить обработку дополнительных, в DZone есть отличная статья Брайана Вермеера, использующая концепцию Either. Это показывает отличный способ справиться с этой ситуацией. Не хватает только примера кода. Это образец моего исследования с использованием концепций из этой статьи.
источник