Java 8 Лямбда-функция, которая выдает исключение?


470

Я знаю, как создать ссылку на метод, который имеет Stringпараметр и возвращает int, это:

Function<String, Integer>

Однако, это не работает, если функция выдает исключение, скажем, что оно определено как:

Integer myMethod(String s) throws IOException

Как бы я определил эту ссылку?





4
Все решение выглядит так, как если бы, за исключением исключений времени выполнения, я считаю, что это не очень хорошее решение. так что лучше использовать старую java для циклов
Nazeel

5
А как насчет библиотеки Jool ? cf org.jooq.lambda. Непроверенный пакет
chaiyachaiya

Ответы:


403

Вам нужно будет выполнить одно из следующих действий.

  • Если это ваш код, тогда определите свой собственный функциональный интерфейс, который объявляет проверенное исключение:

    @FunctionalInterface
    public interface CheckedFunction<T, R> {
       R apply(T t) throws IOException;
    }

    и использовать это:

    void foo (CheckedFunction f) { ... }
  • В противном случае, включите Integer myMethod(String s)метод, который не объявляет проверенное исключение:

    public Integer myWrappedMethod(String s) {
        try {
            return myMethod(s);
        }
        catch(IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    а потом:

    Function<String, Integer> f = (String t) -> myWrappedMethod(t);

    или:

    Function<String, Integer> f =
        (String t) -> {
            try {
               return myMethod(t);
            }
            catch(IOException e) {
                throw new UncheckedIOException(e);
            }
        };

7
Вы можете расширить Consumerили Functionиспользовать методы по умолчанию - см. Мой ответ ниже.
JLB

2
Я думаю, что это может быть достигнуто как одна строка .
Нед Твигг

6
Незначительная оптимизация: вместо (String t) -> myWrappedMethod(t)ссылки на метод this::myWrappedMethodтакже можно использовать.
Clashsoft

8
Еще более общий способ сделать это - определить проверенную функцию, например, открытый интерфейс @FunctionalInterface. CheckedFunction <T, R, E extends Exception> {R apply (T t) throws E; } Таким образом, вы также можете определить, какое исключение выдает функция, и можете повторно использовать интерфейс для любого кода.
Мартин Одхелиус,

3
Ух ты. Ява хуже, чем я думал
user275801

194

Вы можете расширить ConsumerFunctionт. Д.) Новым интерфейсом, который обрабатывает исключения - используя стандартные методы Java 8 !

Рассмотрим этот интерфейс (расширяет Consumer):

@FunctionalInterface
public interface ThrowingConsumer<T> extends Consumer<T> {

    @Override
    default void accept(final T elem) {
        try {
            acceptThrows(elem);
        } catch (final Exception e) {
            // Implement your own exception handling logic here..
            // For example:
            System.out.println("handling an exception...");
            // Or ...
            throw new RuntimeException(e);
        }
    }

    void acceptThrows(T elem) throws Exception;

}

Тогда, например, если у вас есть список:

final List<String> list = Arrays.asList("A", "B", "C");

Если вы хотите использовать его (например, с forEach) с некоторым кодом, который генерирует исключения, вы бы традиционно установили блок try / catch:

final Consumer<String> consumer = aps -> {
    try {
        // maybe some other code here...
        throw new Exception("asdas");
    } catch (final Exception ex) {
        System.out.println("handling an exception...");
    }
};
list.forEach(consumer);

Но с этим новым интерфейсом вы можете создать его с помощью лямбда-выражения, и компилятор не будет жаловаться:

final ThrowingConsumer<String> throwingConsumer = aps -> {
    // maybe some other code here...
    throw new Exception("asdas");
};
list.forEach(throwingConsumer);

Или даже просто брось, чтобы быть более лаконичным!

list.forEach((ThrowingConsumer<String>) aps -> {
    // maybe some other code here...
    throw new Exception("asda");
});

Обновление : Похоже, что есть очень хорошая часть библиотеки утилит Durian под названием Errors, которую можно использовать для решения этой проблемы с гораздо большей гибкостью. Например, в моей реализации выше я явно определил политику обработки ошибок ( System.out...или throw RuntimeException), тогда как ошибки Дуриана позволяют применять политику на лету с помощью большого набора утилитарных методов. Спасибо, что поделились этим , @NedTwigg !.

Пример использования:

list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c)));

14
Таким образом, у вас есть набор интерфейсов (Function, Consumer, Supplier, ...) и набор политик для обработки ошибок (Throwing, System.out.println, ...). Я думаю, что есть способ облегчить использование любой политики с любой функцией, без необходимости копировать вставить «ThrowingConsumer, ThrowingFunction и т. Д.».
Нед Твигг

1
Некоторое время спустя ... Я решил использовать непроверенные исключения и не использовать никаких дополнительных функциональных интерфейсов или новых библиотек -> легкий путь, меньше ввода, быстрая доставка, не так ли?
Алиопи

1
Вот улучшенная версия, использующая скрытную метание. Не нужно разворачивать RuntimeException в CheckException.
17

62

Я думаю, что Errorsкласс Дуриана сочетает в себе многие плюсы различных предложений выше.

Чтобы включить Дуриан в ваш проект, вы можете:

  • возьмите его из центра jcenter или maven вcom.diffplug.durian:durian:3.3.0
  • или просто скопировать вставить только две небольшие классы в вашем коде: Throwing.javaиErrors.java

Или вы можете просто использовать RxJava, так как потоки нуждаются в неотъемлемой обработке ошибок, и если в вашем конвейере есть что-то, что выдает исключение, есть большая вероятность, что это, вероятно, наблюдаемый поток. Это также не навязывает Java 8 нижестоящим потребителям библиотеки.
Адам Гент

2
Обратите внимание, что у Durian нет новых версий с июня 2016 года. Не ограничитель шоу, но кое-что, о чем следует помнить.
Иштван Девай

9
Дуриан здесь. Что сломано? Если пользователь обнаружит ошибку или важную отсутствующую функцию, мы быстро выпустим исправление. Библиотека проста, поэтому у нас не было отчетов об ошибках, поэтому нам не нужно было выпускать исправления.
Нед Твигг

28

Это не относится к Java 8. Вы пытаетесь скомпилировать что-то эквивалентное:

interface I {
    void m();
}
class C implements I {
    public void m() throws Exception {} //can't compile
}

15
Вопрос «Как бы я определил эту ссылку?» , Это на самом деле не отвечает на вопрос; это только разъясняет, в чем проблема.
Дауд ибн Карим

13

Отказ от ответственности: я еще не использовал Java 8, только читал об этом.

Function<String, Integer>не бросает IOException, так что вы не можете поместить в него какой-либо код throws IOException. Если вы вызываете метод, который ожидает a Function<String, Integer>, то лямбда, которую вы передаете этому методу, не может генерировать IOException, точка. Вы можете написать лямбда-код следующим образом (я думаю, что это синтаксис лямбды, не уверен):

(String s) -> {
    try {
        return myMethod(s);
    } catch (IOException ex) {
        throw new RuntimeException(ex);
        // (Or do something else with it...)
    }
}

Или, если метод, которому вы передаете лямбду, является тем, который вы написали сами, вы можете определить новый функциональный интерфейс и использовать его в качестве типа параметра вместо Function<String, Integer>:

public interface FunctionThatThrowsIOException<I, O> {
    O apply(I input) throws IOException;
}

добавьте аннотацию @FunctionalInterface перед вашим интерфейсом, только тогда она будет использоваться для лямбд.
Гангнус

13
@Gangnus: @FunctionalInterfaceаннотация не обязательна, чтобы ее можно было использовать для лямбд. Рекомендуется для проверки работоспособности.
Танмай Патил

9

Если вы не против использовать стороннюю библиотеку ( Vavr ), вы можете написать

CheckedFunction1<String, Integer> f = this::myMethod;

Он также имеет так называемую монаду Try, которая обрабатывает ошибки:

Try(() -> f.apply("test")) // results in a Success(Integer) or Failure(Throwable)
        .map(i -> ...) // only executed on Success
        ...

Пожалуйста, прочитайте больше здесь .

Отказ от ответственности: я создатель Vavr.


7

Вы можете использовать Unthrow обертку

Function<String, Integer> func1 = s -> Unthrow.wrap(() -> myMethod(s));

или

Function<String, Integer> func2 = s1 -> Unthrow.wrap((s2) -> myMethod(s2), s1);

6

Однако вы можете создать свой собственный FunctionalInterface, который выдает, как показано ниже.

@FunctionalInterface
public interface UseInstance<T, X extends Throwable> {
  void accept(T instance) throws X;
}

затем реализуйте его, используя Lambdas или ссылки, как показано ниже.

import java.io.FileWriter;
import java.io.IOException;

//lambda expressions and the execute around method (EAM) pattern to
//manage resources

public class FileWriterEAM  {
  private final FileWriter writer;

  private FileWriterEAM(final String fileName) throws IOException {
    writer = new FileWriter(fileName);
  }
  private void close() throws IOException {
    System.out.println("close called automatically...");
    writer.close();
  }
  public void writeStuff(final String message) throws IOException {
    writer.write(message);
  }
  //...

  public static void use(final String fileName, final UseInstance<FileWriterEAM, IOException> block) throws IOException {

    final FileWriterEAM writerEAM = new FileWriterEAM(fileName);    
    try {
      block.accept(writerEAM);
    } finally {
      writerEAM.close();
    }
  }

  public static void main(final String[] args) throws IOException {

    FileWriterEAM.use("eam.txt", writerEAM -> writerEAM.writeStuff("sweet"));

    FileWriterEAM.use("eam2.txt", writerEAM -> {
        writerEAM.writeStuff("how");
        writerEAM.writeStuff("sweet");      
      });

    FileWriterEAM.use("eam3.txt", FileWriterEAM::writeIt);     

  }


 void writeIt() throws IOException{
     this.writeStuff("How ");
     this.writeStuff("sweet ");
     this.writeStuff("it is");

 }

}

6

Вы можете.

Расширяя @marcg UtilExceptionи добавляя generic, <E extends Exception>где это необходимо: таким образом, компилятор снова заставит вас добавлять предложения throw, и все будет так, как если бы вы могли изначально генерировать отмеченные исключения в потоках java 8.

public final class LambdaExceptionUtil {

    @FunctionalInterface
    public interface Function_WithExceptions<T, R, E extends Exception> {
        R apply(T t) throws E;
    }

    /**
     * .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) {
                throwActualException(exception);
                return null;
            }
        };
    }

    @SuppressWarnings("unchecked")
    private static <E extends Exception> void throwActualException(Exception exception) throws E {
        throw (E) exception;
    }

}

public class LambdaExceptionUtilTest {

    @Test
    public void testFunction() throws MyTestException {
        List<Integer> sizes = Stream.of("ciao", "hello").<Integer>map(rethrowFunction(s -> transform(s))).collect(toList());
        assertEquals(2, sizes.size());
        assertEquals(4, sizes.get(0).intValue());
        assertEquals(5, sizes.get(1).intValue());
    }

    private Integer transform(String value) throws MyTestException {
        if(value==null) {
            throw new MyTestException();
        }
        return value.length();
    }

    private static class MyTestException extends Exception { }
}

5

У меня была эта проблема с Class.forName и Class.newInstance внутри лямбды, поэтому я просто сделал:

public Object uncheckedNewInstanceForName (String name) {

    try {
        return Class.forName(name).newInstance();
    }
    catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

Внутри лямбды вместо вызова Class.forName («myClass»). NewInstance () я просто вызвал uncheckedNewInstanceForName («myClass»)


4

Другое решение, использующее обертку Function, - вернуть либо экземпляр обертки вашего результата, скажем, Success, если все прошло хорошо, либо экземпляр, скажем, Failure.

Некоторый код, чтобы уточнить вещи:

public interface ThrowableFunction<A, B> {
    B apply(A a) throws Exception;
}

public abstract class Try<A> {

    public static boolean isSuccess(Try tryy) {
        return tryy instanceof Success;
    }

    public static <A, B> Function<A, Try<B>> tryOf(ThrowableFunction<A, B> function) {
        return a -> {
            try {
                B result = function.apply(a);
                return new Success<B>(result);
            } catch (Exception e) {
                return new Failure<>(e);
            }
        };
    }

    public abstract boolean isSuccess();

    public boolean isError() {
        return !isSuccess();
    }

    public abstract A getResult();

    public abstract Exception getError();
}

public class Success<A> extends Try<A> {

    private final A result;

    public Success(A result) {
        this.result = result;
    }

    @Override
    public boolean isSuccess() {
        return true;
    }

    @Override
    public A getResult() {
        return result;
    }

    @Override
    public Exception getError() {
        return new UnsupportedOperationException();
    }

    @Override
    public boolean equals(Object that) {
        if(!(that instanceof Success)) {
            return false;
        }
        return Objects.equal(result, ((Success) that).getResult());
    }
}

public class Failure<A> extends Try<A> {

    private final Exception exception;

    public Failure(Exception exception) {
        this.exception = exception;
    }

    @Override
    public boolean isSuccess() {
        return false;
    }

    @Override
    public A getResult() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Exception getError() {
        return exception;
    }
}

Простой вариант использования:

List<Try<Integer>> result = Lists.newArrayList(1, 2, 3).stream().
    map(Try.<Integer, Integer>tryOf(i -> someMethodThrowingAnException(i))).
    collect(Collectors.toList());

4

Эта проблема беспокоила меня также; Вот почему я создал этот проект .

С этим вы можете сделать:

final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;

Всего имеется 39 интерфейсов, определенных JDK, которые имеют такой Throwingэквивалент; все они @FunctionalInterfaceиспользуются в потоках (база, Streamно также IntStream, LongStreamиDoubleStream ).

И так как каждый из них расширяет свой не бросающий аналог, вы можете напрямую использовать их и в лямбдах:

myStringStream.map(f) // <-- works

Поведение по умолчанию состоит в том, что, когда ваша бросающая лямбда генерирует проверенное исключение, ThrownByLambdaExceptionгенерируется проверенное исключение в качестве причины. Поэтому вы можете уловить это и получить причину.

Другие функции также доступны.


Мне очень нравится эта идея, я только хотел бы, чтобы вы сделали общие элементы throwables как предложено здесь: javaspecialists.eu/archive/Issue221.html , например: @FunctionalInterface public interface SupplierWithCE<T, X extends Exception> { T get() throws X; }- таким образом, пользователь не должен перехватывать Throwable, а вместо этого проверять исключение.
Золтан

@ Золтан, но было бы больно каждый раз объявлять об исключении; Кроме того, вы всегда можете просто использовать, скажем, .apply () вместо .doApply () и catch ThrownByLambdaException, у вас будет исходное исключение в качестве причины (или вы можете использовать rethrow(...).as(MyRuntimeException.class))
fge

Я думаю, что есть (вроде) способ обойти это .
Нед Твигг

@NedTwigg Я уже давно решил это; Теперь я могу использовать Throwing.runnable()и другие, всегда с возможностью цепочки
fge

Функциональность цепочки очень крутая! Мой комментарий был о том, должно ли ThrowingRunnable иметь общее исключение или нет. Золтан спросил, может ли ваша библиотека иметь аргумент в качестве универсального параметра, и вы ответили, что будет неудобно использовать. Моя ссылка была на некоторые строки кода, которые показывают один из способов сделать исключения универсальными, без каких-либо проблем. Если я не прочитал это, исключения в вашей библиотеке не являются общими (что является разумным выбором дизайна, потому что вы не получаете большой пользы, делая их общими).
Нед Твигг

4

Здесь уже опубликовано много отличных отзывов. Просто пытаюсь решить проблему с другой точки зрения. Это только мои 2 цента, пожалуйста, поправьте меня, если я где-то не прав.

Предложение Throws в FunctionalInterface не очень хорошая идея

Я думаю, что это, вероятно, не очень хорошая идея, чтобы принудительно генерировать IOException по следующим причинам.

  • Это выглядит как анти-паттерн для Stream / Lambda. Вся идея заключается в том, что вызывающая сторона решит, какой код предоставить и как обработать исключение. Во многих сценариях IOException может быть неприменимо для клиента. Например, если клиент получает значение из кеша / памяти вместо фактического ввода-вывода.

  • Кроме того, обработка исключений в потоках становится действительно отвратительной. Например, вот мой код будет выглядеть, если я использую ваш API

               acceptMyMethod(s -> {
                    try {
                        Integer i = doSomeOperation(s);
                        return i;
                    } catch (IOException e) {
                        // try catch block because of throws clause
                        // in functional method, even though doSomeOperation
                        // might not be throwing any exception at all.
                        e.printStackTrace();
                    }
                    return null;
                });

    Ужасно не так ли? Более того, как я уже упоминал в моем первом пункте, метод doSomeOperation может вызывать или не вызывать IOException (в зависимости от реализации клиента / вызывающей стороны), но из-за предложения throws в вашем методе FunctionalInterface мне всегда приходится писать Попробуйте поймать.

Что мне делать, если я действительно знаю, что этот API выдает IOException

  • Тогда, вероятно, мы путаем FunctionalInterface с типичными интерфейсами. Если вы знаете, что этот API вызовет IOException, то, скорее всего, вам также известно некоторое поведение по умолчанию / абстрактное поведение. Я думаю, что вы должны определить интерфейс и развернуть свою библиотеку (с реализацией по умолчанию / абстрактно) следующим образом

    public interface MyAmazingAPI {
        Integer myMethod(String s) throws IOException;
    }

    Но проблема try-catch все еще существует для клиента. Если я использую ваш API в потоке, мне все равно нужно обрабатывать IOException в отвратительном блоке try-catch.

  • Предоставьте API, дружественный к потоку по умолчанию, следующим образом

    public interface MyAmazingAPI {
        Integer myMethod(String s) throws IOException;
    
        default Optional<Integer> myMethod(String s, Consumer<? super Exception> exceptionConsumer) {
            try {
                return Optional.ofNullable(this.myMethod(s));
            } catch (Exception e) {
                if (exceptionConsumer != null) {
                    exceptionConsumer.accept(e);
                } else {
                    e.printStackTrace();
                }
            }
    
            return Optional.empty();
        }
    }

    Метод по умолчанию принимает в качестве аргумента объект-потребитель, который будет отвечать за обработку исключения. Теперь, с точки зрения клиента, код будет выглядеть так

    strStream.map(str -> amazingAPIs.myMethod(str, Exception::printStackTrace))
                    .filter(Optional::isPresent)
                    .map(Optional::get).collect(toList());

    Хорошо, верно? Конечно, вместо Exception :: printStackTrace можно использовать регистратор или другую логику обработки.

  • Вы также можете предоставить метод, аналогичный https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function- . Это означает, что вы можете предоставить другой метод, который будет содержать исключение из предыдущего вызова метода. Недостатком является то, что вы теперь делаете свои API-интерфейсы с состоянием, что означает, что вам нужно управлять безопасностью потоков, что в конечном итоге приведет к снижению производительности. Просто вариант, чтобы рассмотреть, хотя.


Я согласен с тем, что преобразование проверенного Исключения в непроверенное Исключение или проглатывание Исключения не является хорошей идеей, поскольку невозможно узнать, какой элемент Streamвызвал Исключение. Таким образом, мне нравится идея иметь обработчик исключений и фильтровать недопустимые результаты. Обратите внимание, что ваш MyAmazingAPI является по сути FunctionalInterface(поэтому вы можете добавить аннотацию @FunctionalInterface). Также вы можете использовать значение по умолчанию вместо использования Optional.empty().
Жюльен Кронегг

4

Идиома подлый бросок позволяет обойти CheckedExceptionлямбда-выражение. Упаковка CheckedExceptionв RuntimeExceptionне подходит для строгой обработки ошибок.

Его можно использовать как Consumerфункцию, используемую в коллекции Java.

Вот простая и улучшенная версия ответа Jib .

import static Throwing.rethrow;

@Test
public void testRethrow() {
    thrown.expect(IOException.class);
    thrown.expectMessage("i=3");

    Arrays.asList(1, 2, 3).forEach(rethrow(e -> {
        int i = e.intValue();
        if (i == 3) {
            throw new IOException("i=" + i);
        }
    }));
}

Это просто переворачивает лямбду в перебрасывании . Это делает CheckedExceptionотбрасывание любого, Exceptionчто было брошено в вашу лямбду.

public final class Throwing {
    private Throwing() {}

    @Nonnull
    public static <T> Consumer<T> rethrow(@Nonnull final ThrowingConsumer<T> consumer) {
        return consumer;
    }

    /**
     * The compiler sees the signature with the throws T inferred to a RuntimeException type, so it
     * allows the unchecked exception to propagate.
     * 
     * http://www.baeldung.com/java-sneaky-throws
     */
    @SuppressWarnings("unchecked")
    @Nonnull
    public static <E extends Throwable> void sneakyThrow(@Nonnull Throwable ex) throws E {
        throw (E) ex;
    }

}

Найдите полный код и юнит-тесты здесь .


3

Вы можете использовать ET для этого. ET - это небольшая библиотека Java 8 для преобразования / перевода исключений.

С ET это выглядит так:

// Do this once
ExceptionTranslator et = ET.newConfiguration().done();

...

// if your method returns something
Function<String, Integer> f = (t) -> et.withReturningTranslation(() -> myMethod(t));

// if your method returns nothing
Consumer<String> c = (t) -> et.withTranslation(() -> myMethod(t));

ExceptionTranslatorэкземпляры являются потокобезопасными и могут совместно использоваться несколькими компонентами. Вы можете настроить более конкретные правила преобразования исключений (например FooCheckedException -> BarRuntimeException), если хотите. Если другие правила недоступны, отмеченные исключения автоматически преобразуются в RuntimeException.

(Отказ от ответственности: я автор ET)


2
Похоже, вы автор этой библиотеки. Согласно правилам SO , вы должны раскрывать свою принадлежность в своих ответах. Пожалуйста, добавьте к своему ответу явно, что вы написали эту библиотеку (то же самое для других ответов, связанных с ET).
Тагир Валеев

2
Привет, Тагир, спасибо за подсказку. Я обновил ответ.
Миха

2

То, что я делаю, это позволяет пользователю дать значение, которое он на самом деле хочет в случае исключения. Так что я что-то похож на это

public static <T, R> Function<? super T, ? extends R> defaultIfThrows(FunctionThatThrows<? super T, ? extends R> delegate, R defaultValue) {
    return x -> {
        try {
            return delegate.apply(x);
        } catch (Throwable throwable) {
            return defaultValue;
        }
    };
}

@FunctionalInterface
public interface FunctionThatThrows<T, R> {
    R apply(T t) throws Throwable;
}

И тогда это можно назвать как:

defaultIfThrows(child -> child.getID(), null)

1
Это расширение этой идеи, которое проводит различие между стратегией «значение по умолчанию» (как в вашем ответе) и стратегией «перезапуска RuntimeException», где значение по умолчанию не требуется.
Нед Твигг

2

Если вы не возражаете против использования сторонней библиотеки, с Cylops-реагировать , библиотекой, в которую я добавляю , вы можете использовать API FluentFunctions для написания

 Function<String, Integer> standardFn = FluentFunctions.ofChecked(this::myMethod);

ofChecked принимает jOOλ CheckedFunction и возвращает смягченную ссылку обратно в стандартную (непроверенную) JDK java.util.function.Function.

В качестве альтернативы вы можете продолжать работать с захваченной функцией через API FluentFunctions!

Например, чтобы выполнить свой метод, повторить его до 5 раз и записать его статус, вы можете написать

  FluentFunctions.ofChecked(this::myMethod)
                 .log(s->log.debug(s),e->log.error(e,e.getMessage())
                 .try(5,1000)
                 .apply("my param");

2

По умолчанию функция Java 8 не позволяет генерировать исключения, и, как предлагается в нескольких ответах, есть много способов ее достижения, один из них:

@FunctionalInterface
public interface FunctionWithException<T, R, E extends Exception> {
    R apply(T t) throws E;
}

Определить как:

private FunctionWithException<String, Integer, IOException> myMethod = (str) -> {
    if ("abc".equals(str)) {
        throw new IOException();
    }
  return 1;
};

И добавить throwsили try/catchто же исключение в вызывающем методе.


2

Создайте пользовательский тип возвращаемого значения, который будет распространять проверенное исключение. Это альтернатива созданию нового интерфейса, который отражает существующий функциональный интерфейс с небольшой модификацией «исключения исключения» в методе функционального интерфейса.

Определение

CheckedValueSupplier

public static interface CheckedValueSupplier<V> {
    public V get () throws Exception;
}

CheckedValue

public class CheckedValue<V> {
    private final V v;
    private final Optional<Exception> opt;

    public Value (V v) {
        this.v = v;
    }

    public Value (Exception e) {
        this.opt = Optional.of(e);
    }

    public V get () throws Exception {
        if (opt.isPresent()) {
            throw opt.get();
        }
        return v;
    }

    public Optional<Exception> getException () {
        return opt;
    }

    public static <T> CheckedValue<T> returns (T t) {
        return new CheckedValue<T>(t);
    }

    public static <T> CheckedValue<T> rethrows (Exception e) {
        return new CheckedValue<T>(e);
    }

    public static <V> CheckedValue<V> from (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            return Result.rethrows(e);
        }
    }

    public static <V> CheckedValue<V> escalates (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

Применение

//  Don't use this pattern with FileReader, it's meant to be an
//  example.  FileReader is a Closeable resource and as such should
//  be managed in a try-with-resources block or in another safe
//  manner that will make sure it is closed properly.

//  This will not compile as the FileReader constructor throws
//  an IOException.
    Function<String, FileReader> sToFr =
        (fn) -> new FileReader(Paths.get(fn).toFile());

// Alternative, this will compile.
    Function<String, CheckedValue<FileReader>> sToFr = (fn) -> {
        return CheckedValue.from (
            () -> new FileReader(Paths.get("/home/" + f).toFile()));
    };

// Single record usage
    // The call to get() will propagate the checked exception if it exists.
    FileReader readMe = pToFr.apply("/home/README").get();


// List of records usage
    List<String> paths = ...; //a list of paths to files
    Collection<CheckedValue<FileReader>> frs =
        paths.stream().map(pToFr).collect(Collectors.toList());

// Find out if creation of a file reader failed.
    boolean anyErrors = frs.stream()
        .filter(f -> f.getException().isPresent())
        .findAny().isPresent();

В чем дело?

Создается единственный функциональный интерфейс, который выдает проверенное исключение ( CheckedValueSupplier). Это будет единственный функциональный интерфейс, который позволяет проверять исключения. Все остальные функциональные интерфейсы будут использоватьCheckedValueSupplier для переноса любой код, который выдает проверенное исключение.

CheckedValueКласс будет содержать результат выполнения какой - либо логики, генерирующий проверяемое исключение. Это предотвращает распространение проверенного исключения до точки, в которой код пытается получить доступ к значению, которое экземплярCheckedValue .

Проблемы с этим подходом.

  • Теперь мы выкидываем «Исключение», эффективно скрывая определенный тип первоначально брошенного.
  • Мы не знаем, что произошло исключение, пока не CheckedValue#get()будет вызвано.

Потребитель и др.

Некоторые функциональные интерфейсы (Consumer например) должны обрабатываться другим способом, поскольку они не обеспечивают возвращаемое значение.

Функция вместо потребителя

Один из подходов заключается в использовании функции вместо потребителя, которая применяется при обработке потоков.

    List<String> lst = Lists.newArrayList();
// won't compile
lst.stream().forEach(e -> throwyMethod(e));
// compiles
lst.stream()
    .map(e -> CheckedValueSupplier.from(
        () -> {throwyMethod(e); return e;}))
    .filter(v -> v.getException().isPresent()); //this example may not actually run due to lazy stream behavior

обострять

Кроме того, вы всегда можете перейти к RuntimeException. Существуют и другие ответы, которые охватывают эскалацию проверенного исключения изнутри a Consumer.

Не потреблять

Просто избегайте функциональных интерфейсов и используйте петлю for в хорошем стиле.


2

Я использую перегруженную служебную функцию, unchecked()которая называется несколько вариантов использования.


НЕКОТОРЫЕ ПРИМЕРНЫЕ ИСПОЛЬЗОВАНИЯ

unchecked(() -> new File("hello.txt").createNewFile());

boolean fileWasCreated = unchecked(() -> new File("hello.txt").createNewFile());

myFiles.forEach(unchecked(file -> new File(file.path).createNewFile()));

СЛУЖБА ПОДДЕРЖКИ

public class UncheckedUtils {

    @FunctionalInterface
    public interface ThrowingConsumer<T> {
        void accept(T t) throws Exception;
    }

    @FunctionalInterface
    public interface ThrowingSupplier<T> {
        T get() throws Exception;
    }

    @FunctionalInterface
    public interface ThrowingRunnable {
        void run() throws Exception;
    }

    public static <T> Consumer<T> unchecked(
            ThrowingConsumer<T> throwingConsumer
    ) {
        return i -> {
            try {
                throwingConsumer.accept(i);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        };
    }

    public static <T> T unchecked(
            ThrowingSupplier<T> throwingSupplier
    ) {
        try {
            return throwingSupplier.get();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void unchecked(
            ThrowingRunnable throwing
    ) {
        try {
            throwing.run();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}

0

Некоторые из предложенных решений используют общий аргумент E для передачи типа исключения, которое выдается.

Сделайте еще один шаг, и вместо того, чтобы передавать тип исключения, передайте Consumer типа исключения, как в ...

Consumer<E extends Exception>

Вы можете создать несколько вариантов многократного использования, Consumer<Exception>которые будут соответствовать общим потребностям обработки вашего приложения.


0

Я сделаю что-то общее:

public interface Lambda {

    @FunctionalInterface
    public interface CheckedFunction<T> {

        T get() throws Exception;
    }

    public static <T> T handle(CheckedFunction<T> supplier) {
        try {
            return supplier.get();
        } catch (Exception exception) {
            throw new RuntimeException(exception);

        }
    }
}

Применение:

 Lambda.handle(() -> method());

0

Используйте Jool Libraryили говорите jOOλ libraryотJOOQ . Он не только предоставляет непроверенные интерфейсы для обработки исключений, но также предоставляет классу Seq множество полезных методов.

Кроме того, он содержит функциональные интерфейсы с 16 параметрами. Кроме того, он предоставляет класс Tuple, который используется в различных сценариях.

Jool Git Link

В частности, поиск в библиотеке для org.jooq.lambda.fi.util.functionпакета. Он содержит все интерфейсы из Java-8 с предварительно установленным флажком. См. Ниже для справки: -

введите описание изображения здесь


0

Я являюсь автором крошечной библиотеки с некоторой общей магией, позволяющей создавать любые исключения Java в любом месте без необходимости их перехвата или переносаRuntimeException .

Применение: unchecked(() -> methodThrowingCheckedException())

public class UncheckedExceptions {

    /**
     * throws {@code exception} as unchecked exception, without wrapping exception.
     *
     * @return will never return anything, return type is set to {@code exception} only to be able to write <code>throw unchecked(exception)</code>
     * @throws T {@code exception} as unchecked exception
     */
    @SuppressWarnings("unchecked")
    public static <T extends Throwable> T unchecked(Exception exception) throws T {
        throw (T) exception;
    }


    @FunctionalInterface
    public interface UncheckedFunction<R> {
        R call() throws Exception;
    }

    /**
     * Executes given function,
     * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
     *
     * @return result of function
     * @see #unchecked(Exception)
     */
    public static <R> R unchecked(UncheckedFunction<R> function) {
        try {
            return function.call();
        } catch (Exception e) {
            throw unchecked(e);
        }
    }


    @FunctionalInterface
    public interface UncheckedMethod {
        void call() throws Exception;
    }

    /**
     * Executes given method,
     * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
     *
     * @see #unchecked(Exception)
     */
    public static void unchecked(UncheckedMethod method) {
        try {
            method.call();
        } catch (Exception e) {
            throw unchecked(e);
        }
    }
}

источник: https://github.com/qoomon/unchecked-exceptions-java


-7
public void frankTest() {
    int pageId= -1;

    List<Book> users= null;
    try {
        //Does Not Compile:  Object page=DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> new Portal(rw.getInt("id"), "", users.parallelStream().filter(uu -> uu.getVbid() == rw.getString("user_id")).findFirst().get(), rw.getString("name")));

        //Compiles:
        Object page= DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> { 
            try {
                final Book bk= users.stream().filter(bp -> { 
                    String name= null;
                    try {
                        name = rw.getString("name");
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    return bp.getTitle().equals(name); 
                }).limit(1).collect(Collectors.toList()).get(0);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return new Portal(rw.getInt("id"), "", users.get(0), rw.getString("name")); 
        } );
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

3
Хотите прокомментировать свою работу? Ответы только на код не так полезны.
Phantômaxx

@Franky, вы можете исправить свою презентацию, используя 4 интервала вместо <code>/<code>:)
AdrieanKhisbe
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.