Функція Java 8 Lambda, яка кидає виняток?


469

Я знаю, як створити посилання на метод, який має Stringпараметр і повертає an int, це:

Function<String, Integer>

Однак це не працює, якщо функція кидає виняток, скажімо, вона визначена як:

Integer myMethod(String s) throws IOException

Як би я визначив цю посилання?





4
Все рішення виглядає як щось, як, викидаючи винятки з виконання, я вірю, що це не гарне рішення. так що краще використовувати стару Java для циклів
Nazeel

5
А що з бібліотекою jool ? cf org.jooq.lambda.Перевірений пакет
chaiyachaiya

Відповіді:


402

Вам потрібно буде зробити одне з наступних дій.

  • Якщо це ваш код, то визначте власний функціональний інтерфейс, який оголошує перевірений виняток:

    @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 розширює виняток> {R apply (T t) кидає E; } Таким чином ви також можете визначити, який виняток кидає функція, і можете повторно використовувати інтерфейс для будь-якого коду.
Мартін Оделій

3
Ого. Java гірша, ніж я думав
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
Таким чином, у вас є набір інтерфейсів (функція, споживач, постачальник, ...) та набір політик для керування помилками (Throwing, System.out.println, ...). Я думаю, що є спосіб полегшити використання будь-якої політики з будь-якими функціями, без копіювання пасти "ThrowingConsumer, ThrowingFunction тощо".
Нед Твігг

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

1
Ось вдосконалена версія з використанням підліткових ідіом. Немає необхідності розгортати RuntimeException у CheckException.
myui

61

Я думаю , що Errorsклас Дуріана поєднує в собі багато плюсів різних пропозицій вище.

Щоб включити Дуріан у свій проект, ви можете:

  • візьміть його з jcenter або maven central atcom.diffplug.durian:durian:3.3.0
  • або просто скопіюйте у свій код лише два невеликі класи: Throwing.javaіErrors.java

Або ви можете просто скористатися RxJava, оскільки потокам потрібна притаманна помилка, і якщо у вашому трубопроводі є щось, що викидає виняток, є хороший шанс, що це, мабуть, спостережуваний потік. Це також не змушує Java 8 щодо споживачів бібліотеки нижче.
Адам Гент

2
Зауважте, що у Дуріана немає нових версій з червня 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, лише тоді вона стане корисною для лямбда.
Gangnus

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

9

Якщо ви не заперечуєте, щоб скористатись сторонній lib ( Vavr ), ви можете написати

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

У ньому також є так звана Try monad, яка обробляє помилки:

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

Прочитайте більше тут .

Відмова: Я творець Vavr.



6

Однак ви можете створити власний функціональний інтерфейс, який відображається нижче.

@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і додавання загального, <E extends Exception>де це необхідно: таким чином, компілятор змусить вас знову додати пункти закидання, і все так, ніби ви можете кидати перевірені винятки спочатку в потоках 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 () я просто зателефонував uncontNewInstanceForName ("myClass")


4

Іншим рішенням, що використовує обгортку функції, було б повернути або екземпляр обгортки вашого результату, скажімо, «Успіх», якщо все пішло б добре, або екземпляр, скажімо «Невдача».

Деякі коди для уточнення речей:

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викидається як перевірений виняток як причина. Ви можете, таким чином, захопити це і отримати причину.

Доступні й інші функції.


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

@ Zoltán, що болісно буде декларувати виняток кожен раз, хоча; також ви завжди можете просто використовувати, скажімо, .apply () замість .doApply () і ловити ThrownByLambdaException, ви будете мати оригінальний виняток як причину (або можете використовувати rethrow(...).as(MyRuntimeException.class))
fge

Я думаю, що існує такий спосіб .
Нед Твігг

@NedTwigg я це вже давно вирішив; Зараз я можу використовувати Throwing.runnable()та інших, завжди з ланцюжковими можливостями
грудня

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

4

Тут вже розміщено багато чудових відповідей. Просто спроба вирішити проблему з іншої точки зору. Це лише мої 2 копійки, будь ласка, виправте мене, якщо я десь помиляюся.

Застереження про кидання у 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 (залежно від реалізації клієнта / абонента), але через пункт про кидки у вашому методі FunctionalInterface я завжди повинен записувати приміряти.

Що робити, якщо я дійсно знаю, що цей API передає IOException

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

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

    Однак проблема клієнта все ще існує. Якщо я використовую ваш API в потоці, мені все одно потрібно обробляти IOException у бридкому блоці спроб улов.

  • Надайте 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());

    Приємно так? Звичайно, замість винятку :: printStackTrace можна використовувати логгер або іншу логіку обробки.

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


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

4

Підлий кидок ідіоми дозволяє обходити CheckedExceptionвираз Лямбди. Обгортка CheckedExceptionв а RuntimeExceptionне корисна для суворого поводження з помилками.

Його можна використовувати як Consumerфункцію, що використовується в колекції Java.

Ось проста і вдосконалена версія відповіді джибу .

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
Схоже, ви автор цієї бібліотеки. Відповідно до правил ТА , ви повинні розкрити свою приналежність у своїх відповідях. Будь ласка, додайте до своєї відповіді, що ви написали цю бібліотеку (те саме, що стосується інших відповідей, що стосуються ET).
Тагір Валєєв

2
Привіт Тагіре, дякую за підказку. Я оновив відповідь.
micha

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

Якщо ви не проти використовувати бібліотеку сторонніх організацій, із циклоп-реагуванням , бібліотекою, до якої я вношу внесок, ви можете використовувати 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

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

Визначення

ПеревіренийValueSupplier

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

Перевірено

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для обгортання будь-якого коду, який видає перевірений виняток.

The 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

Ескалат

Крім того, ви завжди можете перейти до "a" RuntimeException. Є й інші відповіді, які охоплюють ескалацію перевіреного винятку зсередини Consumer.

Не споживайте.

Просто уникайте функціональних інтерфейсів разом і використовуйте гарну модель для циклу.


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

Деякі із запропонованих рішень використовують загальний аргумент Е, щоб передавати тип виключення, яке потрапляє.

Зробіть цей крок далі і замість того, щоб передати тип винятку, перейдіть до споживача типу винятку, як у ...

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/uncked-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.