FWIW, у меня была такая же проблема с аналогичной настройкой (Windows XP). Тот же код работал без этой проблемы на Mac (OS X).
Я решил проблему, используя постоянное соединение в классе MySQLi.
См. Здесь для получения дополнительной информации: http://php.net/mysqli.persistconns
Убедитесь, что вы знаете обо всех ошибках / предостережениях, связанных с использованием постоянного соединения.
Вы должны поймать исключение до того, как выйдет из лямбда:
s = s.filter(a -> { try { return a.isActive(); }
catch (IOException e) { throw new UncheckedIOException(e); }}});
. Рассмотрите тот факт, что лямбда не оценивается в месте, где вы ее пишете, но при некоторые совершенно несвязанные места, в классе JDK. Таким образом, это будет точка, в которой это исключенное исключение будет выбрано, и в этом месте оно не будет объявлено.
Вы можете справиться с этим, используя обертку вашей лямбды, которая переводит проверенные исключения на непроверенные :
public static <T> T uncheckCall(Callable<T> callable) {
try { return callable.call(); }
catch (RuntimeException e) { throw e; }
catch (Exception e) { throw new RuntimeException(e); }
}
Ваш пример будет написан как
return s.filter(a -> uncheckCall(a::isActive))
.map(Account::getNumber)
.collect(toSet());
В моих проектах я рассматриваю этот вопрос без обертывания; вместо этого я использую метод, который эффективно устраняет проверку исключения компилятором. Излишне говорить, что с этим следует обращаться с осторожностью, и все участники проекта должны знать, что проверенное исключение может появиться там, где оно не объявлено. Это код сантехники:
public static <T> T uncheckCall(Callable<T> callable) {
try { return callable.call(); }
catch (Exception e) { return sneakyThrow(e); }
}
public static void uncheckRun(RunnableExc r) {
try { r.run(); } catch (Exception e) { sneakyThrow(e); }
}
public interface RunnableExc { void run() throws Exception; }
@SuppressWarnings("unchecked")
private static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
throw (T) t;
}
, и вы можете ожидать получить IOException
, брошенное вам в лицо, даже если collect
не объявляет об этом. В большинстве случаев , но не во всех случаях , которые вы хотели бы просто перестроить исключение, и обрабатывать его как общий сбой. Во всех этих случаях ничто не теряется в ясности или правильности. Просто остерегайтесь тех других случаев, когда вы действительно хотите реагировать на исключение на месте. Разработчик не узнает компилятором, что есть IOException
, чтобы поймать там, и компилятор действительно будет жаловаться, если вы попытаетесь его поймать, потому что мы обманули его, полагая, что такое исключение не может быть выброшено.
Вы также можете распространять свою статическую боль с помощью lambdas, поэтому все это выглядит читаемым:
s.filter(a -> propagate(a::isActive))
propagate
здесь принимает java.util.concurrent.Callable
в качестве параметра и преобразует любое исключение, пойманное во время вызова в RuntimeException
. Существует аналогичный метод преобразования Throwables # распространять (Throwable) в Guava.
Этот метод кажется необходимым для цепной связи лямбда, поэтому я надеюсь, что однажды он будет добавлен к одному из популярных libs или это распространяющееся поведение будет по умолчанию.
public class PropagateExceptionsSample {
// a simplified version of Throwables#propagate
public static RuntimeException runtime(Throwable e) {
if (e instanceof RuntimeException) {
return (RuntimeException)e;
}
return new RuntimeException(e);
}
// this is a new one, n/a in public libs
// Callable just suits as a functional interface in JDK throwing Exception
public static <V> V propagate(Callable<V> callable){
try {
return callable.call();
} catch (Exception e) {
throw runtime(e);
}
}
public static void main(String[] args) {
class Account{
String name;
Account(String name) { this.name = name;}
public boolean isActive() throws IOException {
return name.startsWith("a");
}
}
List<Account> accounts = new ArrayList<>(Arrays.asList(new Account("andrey"), new Account("angela"), new Account("pamela")));
Stream<Account> s = accounts.stream();
s
.filter(a -> propagate(a::isActive))
.map(a -> a.name)
.forEach(System.out::println);
}
}
Учитывая эту проблему, я разработал небольшую библиотеку для работы с проверенными исключениями и лямбдами. Пользовательские адаптеры позволяют интегрироваться с существующими функциональными типами:
stream().map(unchecked(URI::new)) //with a static import
Вы можете свернуть свой собственный вариант Stream
, обернув свою лямбда, чтобы выбросить исключенное исключение, а затем позже разблокировать это исключенное исключение в операциях терминала:
@FunctionalInterface
public interface ThrowingPredicate<T, X extends Throwable> {
public boolean test(T t) throws X;
}
@FunctionalInterface
public interface ThrowingFunction<T, R, X extends Throwable> {
public R apply(T t) throws X;
}
@FunctionalInterface
public interface ThrowingSupplier<R, X extends Throwable> {
public R get() throws X;
}
public interface ThrowingStream<T, X extends Throwable> {
public ThrowingStream<T, X> filter(
ThrowingPredicate<? super T, ? extends X> predicate);
public <R> ThrowingStream<T, R> map(
ThrowingFunction<? super T, ? extends R, ? extends X> mapper);
public <A, R> R collect(Collector<? super T, A, R> collector) throws X;
// etc
}
class StreamAdapter<T, X extends Throwable> implements ThrowingStream<T, X> {
private static class AdapterException extends RuntimeException {
public AdapterException(Throwable cause) {
super(cause);
}
}
private final Stream<T> delegate;
private final Class<X> x;
StreamAdapter(Stream<T> delegate, Class<X> x) {
this.delegate = delegate;
this.x = x;
}
private <R> R maskException(ThrowingSupplier<R, X> method) {
try {
return method.get();
} catch (Throwable t) {
if (x.isInstance(t)) {
throw new AdapterException(t);
} else {
throw t;
}
}
}
@Override
public ThrowingStream<T, X> filter(ThrowingPredicate<T, X> predicate) {
return new StreamAdapter<>(
delegate.filter(t -> maskException(() -> predicate.test(t))), x);
}
@Override
public <R> ThrowingStream<R, X> map(ThrowingFunction<T, R, X> mapper) {
return new StreamAdapter<>(
delegate.map(t -> maskException(() -> mapper.apply(t))), x);
}
private <R> R unmaskException(Supplier<R> method) throws X {
try {
return method.get();
} catch (AdapterException e) {
throw x.cast(e.getCause());
}
}
@Override
public <A, R> R collect(Collector<T, A, R> collector) throws X {
return unmaskException(() -> delegate.collect(collector));
}
}
. Тогда вы можете использовать это же точный способ как Stream
:
Stream<Account> s = accounts.values().stream();
ThrowingStream<Account, IOException> ts = new StreamAdapter<>(s, IOException.class);
return ts.filter(Account::isActive).map(Account::getNumber).collect(toSet());
Это решение потребует довольно много шаблонов, поэтому я предлагаю вам взглянуть на библиотеку , которую я уже сделал , что делает именно то, что я описал здесь для всего класса Stream
(и более!).
new StreamBridge<>(ts, IOException.class);
- & gt; new StreamBridge<>(s, IOException.class);
– kevinarpe
25 August 2015 в 12:41
Если вы не возражаете против использования сторонних библиотек, AOL cyclops-react lib, раскрытие :: Я являюсь автором, имеет класс ExceptionSoftener , который может помочь здесь .
s.filter(softenPredicate(a->a.isActive()));
Этот хелпер-класс UtilException
позволяет использовать любые проверенные исключения в потоках Java, например:
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
Примечание Class::forName
выбрасывает ClassNotFoundException
, который отмечен. Сам поток также выбрасывает ClassNotFoundException
, а НЕ НЕИСПРАВЛЯЕТ НЕВОЗМОЖНОЕ исключение.
public final class UtilException {
@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
void accept(T t) throws E;
}
@FunctionalInterface
public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
void accept(T t, U u) throws E;
}
@FunctionalInterface
public interface Function_WithExceptions<T, R, E extends Exception> {
R apply(T t) throws E;
}
@FunctionalInterface
public interface Supplier_WithExceptions<T, E extends Exception> {
T get() throws E;
}
@FunctionalInterface
public interface Runnable_WithExceptions<E extends Exception> {
void run() throws E;
}
/** .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println)); */
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
return t -> {
try { consumer.accept(t); }
catch (Exception exception) { throwAsUnchecked(exception); }
};
}
public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E {
return (t, u) -> {
try { biConsumer.accept(t, u); }
catch (Exception exception) { throwAsUnchecked(exception); }
};
}
/** .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName)) */
public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E {
return t -> {
try { return function.apply(t); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
};
}
/** rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), */
public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) throws E {
return () -> {
try { return function.get(); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
};
}
/** uncheck(() -> Class.forName("xxx")); */
public static void uncheck(Runnable_WithExceptions t)
{
try { t.run(); }
catch (Exception exception) { throwAsUnchecked(exception); }
}
/** uncheck(() -> Class.forName("xxx")); */
public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier)
{
try { return supplier.get(); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
}
/** uncheck(Class::forName, "xxx"); */
public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) {
try { return function.apply(t); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
}
@SuppressWarnings ("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; }
}
Многие другие примеры использования (после статического импорта UtilException
):
@Test
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException {
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.forEach(rethrowConsumer(className -> System.out.println(Class.forName(className))));
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.forEach(rethrowConsumer(System.out::println));
}
@Test
public void test_Function_with_checked_exceptions() throws ClassNotFoundException {
List<Class> classes1
= Stream.of("Object", "Integer", "String")
.map(rethrowFunction(className -> Class.forName("java.lang." + className)))
.collect(Collectors.toList());
List<Class> classes2
= Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
}
@Test
public void test_Supplier_with_checked_exceptions() throws ClassNotFoundException {
Collector.of(
rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))),
StringJoiner::add, StringJoiner::merge, StringJoiner::toString);
}
@Test
public void test_uncheck_exception_thrown_by_method() {
Class clazz1 = uncheck(() -> Class.forName("java.lang.String"));
Class clazz2 = uncheck(Class::forName, "java.lang.String");
}
@Test (expected = ClassNotFoundException.class)
public void test_if_correct_exception_is_still_thrown_by_method() {
Class clazz3 = uncheck(Class::forName, "INVALID");
}
Но не используйте его, прежде чем понимать следующие преимущества, недостатки и ограничения:
• Если вызывающий код обрабатывает проверенное исключение, вы ДОЛЖНЫ добавить его в предложение throws метода который содержит поток. Компилятор больше не заставит вас его добавлять, поэтому его легче забыть.
• Если код вызова уже обрабатывает проверенное исключение, компилятор напомнит вам добавить предложение throws к методу декларация, содержащая поток (если вы этого не скажете: исключение никогда не выбрасывается в тело соответствующего оператора try).
• В любом случае вы не сможете окружить сам поток чтобы поймать проверенное исключение ВНУТРИ метода, который содержит поток (если вы попытаетесь, компилятор скажет: «Исключение никогда не выбрасывается в тело соответствующей инструкции try».
• Если вы вызываете метод, который буквально никогда не может генерировать исключение, которое оно объявляет, тогда вы не должны включать предложение throws. Например: new String (byteArr, «UTF-8») выдает UnsupportedEncodingException, но UTF-8 гарантируется спецификацией Java всегда присутствовать. Здесь объявление бросков является неприятным, и любое решение, чтобы заставить его замолчать с минимальным шаблоном, приветствуется.
• Если вы ненавидите проверенные исключения и чувствуете, что их никогда не следует добавлять на язык Java для начала (растущий количество людей думает так, и я НЕ один из них), то просто не добавляйте исключенное исключение в предложение throws метода, содержащего поток. Таким образом, проверенное исключение будет вести себя так же, как исключение UNchecked.
• Если вы реализуете строгий интерфейс, в котором у вас нет возможности добавить объявление бросков, и все же исключение исключение полностью Соответственно, затем обертывание исключения, чтобы получить привилегию бросить его, приводит к stacktrace с ложными исключениями, которые не дают никакой информации о том, что на самом деле пошло не так. Хорошим примером является Runnable.run (), который не бросает никаких проверенных исключений. В этом случае вы можете не добавлять исключенное исключение в предложение throws метода, содержащего поток.
• В любом случае, если вы решили НЕ добавлять (или забывать добавлять), проверенное исключение в предложение throws метода, содержащего поток, должно знать об этих двух последствиях бросания CHECKED исключений:
1) Код вызова не сможет поймать его по имени (если вы пытаетесь, компилятор скажет: «Исключение никогда не выбрасывается в тело соответствующей инструкции try»). Он будет пузыриться и, вероятно, попадет в основной цикл программы с помощью некоторых «исключений catch» или «catch Throwable», которые могут быть тем, что вы хотите в любом случае.
2) Он нарушает принцип наименьшего удивления: он больше не будет достаточно, чтобы поймать RuntimeException, чтобы быть в состоянии гарантировать обнаружение всех возможных исключений. По этой причине я считаю, что это не должно быть сделано в коде кода, а только в бизнес-коде, который вы полностью контролируете.
В заключение: я считаю, что ограничения здесь несерьезны, а класс UtilException
могут использоваться без страха. Тем не менее, это зависит от вас!
Расширяя решение @marcg, вы обычно можете бросать и ловить проверенное исключение в потоках; то есть компилятор попросит вас поймать / перебросить, так как вы были вне потоков !!
@FunctionalInterface
public interface Predicate_WithExceptions<T, E extends Exception> {
boolean test(T t) throws E;
}
/**
* .filter(rethrowPredicate(t -> t.isActive()))
*/
public static <T, E extends Exception> Predicate<T> rethrowPredicate(Predicate_WithExceptions<T, E> predicate) throws E {
return t -> {
try {
return predicate.test(t);
} catch (Exception exception) {
return throwActualException(exception);
}
};
}
@SuppressWarnings("unchecked")
private static <T, E extends Exception> T throwActualException(Exception exception) throws E {
throw (E) exception;
}
Затем ваш пример будет написан следующим образом (добавление тестов для более четкого отображения):
@Test
public void testPredicate() throws MyTestException {
List<String> nonEmptyStrings = Stream.of("ciao", "")
.filter(rethrowPredicate(s -> notEmpty(s)))
.collect(toList());
assertEquals(1, nonEmptyStrings.size());
assertEquals("ciao", nonEmptyStrings.get(0));
}
private class MyTestException extends Exception { }
private boolean notEmpty(String value) throws MyTestException {
if(value==null) {
throw new MyTestException();
}
return !value.isEmpty();
}
@Test
public void testPredicateRaisingException() throws MyTestException {
try {
Stream.of("ciao", null)
.filter(rethrowPredicate(s -> notEmpty(s)))
.collect(toList());
fail();
} catch (MyTestException e) {
//OK
}
}
Ваш пример может быть записан как:
import utils.stream.Unthrow;
class Bank{
....
public Set<String> getActiveAccountNumbers() {
return accounts.values().stream()
.filter(a -> Unthrow.wrap(() -> a.isActive()))
.map(a -> Unthrow.wrap(() -> a.getNumber()))
.collect(Collectors.toSet());
}
....
}
Класс Unthrow можно взять здесь https://github.com/SeregaLBN/StreamUnthrower
Это не отвечает непосредственно на вопрос (есть много других ответов), но пытается избежать проблемы в первую очередь:
По моему опыту необходимость обработки исключений в Stream
(или другое лямбда-выражение) часто происходит из-за того, что исключения объявляются из методов, где их не следует бросать. Это часто происходит из-за смешивания бизнес-логики с вводом и выводом. Ваш Account
интерфейс является прекрасным примером:
interface Account {
boolean isActive() throws IOException;
String getNumber() throws IOException;
}
Вместо того, чтобы бросать IOException
на каждый геттер, рассмотрите эту схему:
interface AccountReader {
Account readAccount(…) throws IOException;
}
interface Account {
boolean isActive();
String getNumber();
}
Метод AccountReader.readAccount(…)
может читать учетную запись из базы данных или файла или любого другого и вызывать исключение, если это не удается. Он создает объект Account
, который уже содержит все значения, готовые к использованию. Поскольку значения уже загружены readAccount(…)
, геттеры не будут генерировать исключение. Таким образом, вы можете свободно использовать их в лямбдах без необходимости обертывания, маскировки или скрытия исключений.
Конечно, это не всегда возможно сделать так, как я описывал, но часто это происходит, и это приводит к (IMHO):
throws IOException
для использования, но для удовлетворения компилятора Account
неизменяемым и извлечь выгоду из его преимуществ (например, безопасность потоков ) Account
в lambdas (например, в Stream
) Использовать метод #propagate (). Пример реализации не-Guava из Java 8 Блог Sam Beran :
public class Throwables {
public interface ExceptionWrapper<E> {
E wrap(Exception e);
}
public static <T> T propagate(Callable<T> callable) throws RuntimeException {
return propagate(callable, RuntimeException::new);
}
public static <T, E extends Throwable> T propagate(Callable<T> callable, ExceptionWrapper<E> wrapper) throws E {
try {
return callable.call();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw wrapper.wrap(e);
}
}
}
Он может быть разрешен ниже простым кодом с помощью Stream и Try в AbacusUtil :
Stream.of(accounts).filter(a -> Try.call(a::isActive)).map(a -> Try.call(a::getNumber)).toSet();
Раскрытие информации : Я разработчик AbacusUtil
.