Java 8: лямбда-потоки, фільтр методом за винятком


168

У мене є проблема випробувати лямбдаські вирази Java 8. Зазвичай це працює добре, але тепер у мене є методи, які кидають IOExceptions. Найкраще, якщо ви подивитеся на такий код:

class Bank{
    ....
    public Set<String> getActiveAccountNumbers() throws IOException {
        Stream<Account> s =  accounts.values().stream();
        s = s.filter(a -> a.isActive());
        Stream<String> ss = s.map(a -> a.getNumber());
        return ss.collect(Collectors.toSet());
    }
    ....
}

interface Account{
    ....
    boolean isActive() throws IOException;
    String getNumber() throws IOException;
    ....
}

Проблема полягає в тому, що вона не компілюється, тому що я повинен зафіксувати можливі винятки методів isActive- і getNumber. Але навіть якщо я явно використовую блок "try-catch-Block", як показано нижче, він все ще не компілюється, оскільки я не ловлю виняток. Отже, або в JDK є помилка, або я не знаю, як зловити ці Винятки.

class Bank{
    ....
    //Doesn't compile either
    public Set<String> getActiveAccountNumbers() throws IOException {
        try{
            Stream<Account> s =  accounts.values().stream();
            s = s.filter(a -> a.isActive());
            Stream<String> ss = s.map(a -> a.getNumber());
            return ss.collect(Collectors.toSet());
        }catch(IOException ex){
        }
    }
    ....
}

Як я можу змусити його працювати? Може хтось натякне мені на правильне рішення?




4
Проста і правильна відповідь: виловіть виняток всередині лямбда.
Брайан Гец

Відповіді:


211

Ви повинні зловити виняток, перш ніж він уникне лямбда:

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


4
Раніше я бачив, як NettyIO робив "підлий кидок", і хотів викинути крісло у вікно. "Що? Звідки цей вивірений виняток просочився?" Це перший законний випадок використання для підлий кидок, який я ще бачив. Як програміст, ви повинні бути пильними щодо вказівки на підлий кидок. Можливо, краще просто створити інший потоковий інтерфейс / impl, який підтримує перевірений виняток?
kevinarpe

8
Жоден здоровий API не повинен доставляти клієнту незадекларовані перевірені винятки, це точно. В межах API може бути розуміння, що перевірений виняток може просочитися. Вони не заподіюють шкоди, доки вони є лише ще однією ознакою загальної невдачі і збираються зловити і поводитися з ними оптом.
Марко Топольник

5
@kevinarpe Це точна причина, чому підступні кидки - погана ідея. Короткий кругообіг компілятора повинен заплутати майбутніх технічних працівників.
Thorbjørn Ravn Andersen

29
Тільки тому, що вам не подобаються правила, це не означає, що це гарна ідея взяти закон у свої руки. Ваша порада є безвідповідальною, оскільки вона ставить зручність програміста коду над набагато важливішими міркуваннями щодо прозорості та ремонтопридатності програми.
Брайан Гец

34
@brian Просто те, що щось є правилом, не означає, що це гарна ідея. Але я здивований, що ви посилаєтесь на другу частину моєї відповіді як на "пораду", оскільки я вважав, що я став очевидно очевидним, що я пропоную як рішення, і що я пропоную як вигадку в іноземній валюті зацікавленому читачеві, з рясними відмовами.
Марко Тополник

29

Ви також можете поширити свій статичний біль лямбдами, тому вся справа виглядає читабельною:

s.filter(a -> propagate(a::isActive))

propagateтут він отримує java.util.concurrent.Callableяк параметр і перетворює будь-який виняток, що потрапив під час дзвінка, у RuntimeException. Існує подібний метод перетворення Throwables # propagate (Throwable) в Гуаві .

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

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);
    }
}

22

Цей 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");
    }

Але не використовуйте його, перш ніж зрозуміти наступні переваги, недоліки та обмеження :

• Якщо код-виклик повинен обробляти перевірений виняток, ПОВИНЕН додати його до пункту кидків методу, що містить потік. Компілятор більше не змусить вас додавати його, тому простіше забути його.

• Якщо call-код уже обробляє перевірений виняток, компілятор нагадає вам додати пункт кидок до декларації методу, що містить потік (якщо ви цього не зробите, то виняток ніколи не викидається в тіло відповідного оператора спробу ).

• У будь-якому випадку, ви не зможете оточити сам потік, щоб зловити перевірений виняток ВНУТРИ метод, який містить потік (якщо ви спробуєте, компілятор скаже: Виняток ніколи не викидається в тіло відповідного оператора спробу).

• Якщо ви викликаєте метод, який буквально ніколи не може викинути той виняток, який він оголошує, тоді ви не повинні включати пункт про кидки. Наприклад: нова String (byteArr, "UTF-8") кидає UnsupportedEncodingException, але UTF-8 гарантує, що специфікація Java завжди буде присутня. Тут декларація кидків - це неприємність, і будь-яке рішення замовкнути її мінімальними плитами вітається.

• Якщо ви ненавидите перевірені винятки і вважаєте, що їх ніколи не слід додавати до мови Java (для цього зростає кількість людей, і я НЕ один з них), тоді просто не додайте перевірені винятки до кидає пункт методу, який містить потік. Тоді перевірений виняток буде поводитись як виняток, що перевіряється ООН.

• Якщо ви реалізуєте суворий інтерфейс, коли у вас немає можливості додавати декларацію про кидки, і все ж викид винятку цілком доречний, тоді перенесення винятку просто для отримання привілею для його кидання призводить до стек-тракту з помилковими винятками які не дають інформації про те, що насправді пішло не так. Хороший приклад - Runnable.run (), який не кидає перевірених винятків. У цьому випадку ви можете вирішити не додавати перевірений виняток до пункту кидків методу, який містить потік.

• У будь-якому випадку, якщо ви вирішили НЕ додати (або забути додати) перевірений виняток до пункту кидків методу, що містить потік, пам’ятайте про ці 2 наслідки відкидання перевірених винятків:

1) Код виклику не зможе зловити його по імені (якщо ви спробуєте, компілятор скаже: Виняток ніколи не викидається в тіло відповідного оператора спробу). Він буде бульбашком і, ймовірно, буде схоплений у головному циклі програми деяким "catch Exception" або "catch Throwable", який може бути тим, що ви хочете в будь-якому випадку.

2) Це порушує принцип найменшого здивування: його більше не буде достатньо, щоб зловити RuntimeException, щоб мати змогу гарантувати вилов усіх можливих винятків. З цієї причини я вважаю, що це слід робити не в рамковому коді, а лише в бізнес-коді, який ви повністю контролюєте.

На закінчення: я вважаю, що обмеження тут не є серйозними, і UtilExceptionклас можна використовувати без побоювання. Однак, це залежить від вас!


8

Ви потенційно можете розгорнути свій власний 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);->new StreamBridge<>(s, IOException.class);
kevinarpe

1
@kevinarpe Так. Це також мало сказати StreamAdapter.
Джефрі

5

Використовуйте метод #propagate (). Зразок не-гуавської реалізації з Java 8 Blog від Сем Беран :

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);
        }
    }
}

Посилання на блог Java 8 мертве.
Шпигун

4

Це не відповідає безпосередньо на запитання (на це є багато інших відповідей), але намагається уникнути проблеми в першу чергу:

На мій досвід, потреба в обробці винятків у 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(…), getters не кидають винятку. Таким чином, ви можете вільно використовувати їх у лямбдах без необхідності загортати, маскувати або приховувати винятки.

Звичайно, це не завжди можливо зробити так, як я описав, але часто це є, і це призводить до більш чистого коду (IMHO):

  • Краще розділення проблем та дотримання єдиного принципу відповідальності
  • Менше котло: Вам не потрібно захаращувати код throws IOExceptionбез жодної користі, але щоб задовольнити компілятор
  • Поводження з помилками: Ви обробляєте помилки там, де вони трапляються - під час читання з файлу чи бази даних - замість десь посередині вашої бізнес-логіки лише тому, що ви хочете отримати значення полів
  • Можливо, ви зможете зробити Account непорушним і отримати користь від своїх переваг (наприклад, безпека нитки)
  • Вам не потрібні "брудні хитрощі" або обхідні шляхи для використання Accountв лямбдах (наприклад, в а Stream)

4

Це можна вирішити за допомогою простого коду за допомогою Stream і Спробуйте в AbacusUtil :

Stream.of(accounts).filter(a -> Try.call(a::isActive)).map(a -> Try.call(a::getNumber)).toSet();

Розкриття: Я розробник AbacusUtil.


3

Розширюючи рішення @marcg, ви можете нормально кидати і ловити перевірений виняток у Streams; тобто компілятор попросить вас зловити / перекинути так, як ви були поза потоками !!

@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
    }
}

Цей приклад не складається
Роман М

Привіт @RomanM спасибі за вказівку на це: я виправив відсутній тип повернення методом "castActualException". Ми використовуємо це у виробництві, тому я сподіваюся, що він працює і на вашому боці.
PaoloC

3

Щоб правильно додати код обробки IOException (до RuntimeException), ваш метод буде виглядати так:

Stream<Account> s =  accounts.values().stream();

s = s.filter(a -> { try { return a.isActive(); } 
  catch (IOException e) { throw new RuntimeException(e); }});

Stream<String> ss = s.map(a -> { try { return a.getNumber() }
  catch (IOException e) { throw new RuntimeException(e); }});

return ss.collect(Collectors.toSet());

Проблема зараз полягає в тому, що IOExceptionпотрібно буде захопити як RuntimeExceptionі перетворити назад у IOException-, і це додасть ще більше коду до вищевказаного методу.

Навіщо використовувати, Streamколи це можна зробити так само - і метод кидає, IOExceptionтому зайвий код також не потрібен для цього:

Set<String> set = new HashSet<>();
for(Account a: accounts.values()){
  if(a.isActive()){
     set.add(a.getNumber());
  } 
}
return set;

1

Маючи на увазі це питання, я розробив невелику бібліотеку для роботи з перевіреними винятками та лямбдами. Спеціальні адаптери дозволяють інтегруватися з існуючими функціональними типами:

stream().map(unchecked(URI::new)) //with a static import

https://github.com/TouK/ThrowingFunction/


1

Ваш приклад можна записати так:

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



0

Функціональні інтерфейси на Java не декларують будь-якого перевіреного чи неперевіреного винятку. Нам потрібно змінити підпис методів з:

boolean isActive() throws IOException; 
String getNumber() throwsIOException;

До:

boolean isActive();
String getNumber();

Або обробити це з пробним блоком:

public Set<String> getActiveAccountNumbers() {
  Stream<Account> s =  accounts.values().stream();
  s = s.filter(a -> 
    try{
      a.isActive();
    }catch(IOException e){
      throw new RuntimeException(e);
    }
  );
  Stream<String> ss = s.map(a -> 
    try{
      a.getNumber();
    }catch(IOException e){
      throw new RuntimeException(e);
    }
  );
  return ss.collect(Collectors.toSet());
}

Інший варіант - написати власну обгортку або використовувати бібліотеку типу ThrowingFunction. За допомогою бібліотеки нам потрібно лише додати залежність до нашого pom.xml:

<dependency>
    <groupId>pl.touk</groupId>
    <artifactId>throwing-function</artifactId>
    <version>1.3</version>
</dependency>

І використовуйте специфічні класи, такі як ThrowingFunction, ThrowingConsumer, ThrowingPredicate, ThrowingRunnable, ThrowingSupplier.

Наприкінці код виглядає приблизно так:

public Set<String> getActiveAccountNumbers() {
  return accounts.values().stream()
    .filter(ThrowingPredicate.unchecked(Account::isActive))
    .map(ThrowingFunction.unchecked(Account::getNumber))
    .collect(Collectors.toSet());
}

0

Я не бачу жодного способу обробки перевірених винятків у потоці (Java -8), єдиний спосіб, який я застосував, - це вилучення перевірених винятків у потоці та перекидання їх як неперевірене виключення.

        Arrays.stream(VERSIONS)
        .map(version -> TemplateStore.class
                .getClassLoader().getResourceAsStream(String.format(TEMPLATE_FILE_MASK, version)))
        .map(inputStream -> {
            try {
                return ((EdiTemplates) JAXBContext.newInstance(EdiTemplates.class).createUnmarshaller()
                        .unmarshal(inputStream)).getMessageTemplate();
            } catch (JAXBException e) {
                throw new IllegalArgumentException(ERROR, e);
            }})
        .flatMap(Collection::stream)
        .collect(Collectors.toList());

Ви насправді намагаєтесь відповісти на запитання?
Nilambar Sharma

@Nilambar - Що я тут намагаюся сказати, немає способу обробити перевірений виняток під час використання потоку java ... наприкінці всього, що нам потрібно, щоб зловити перевірений і викинути час запуску / неперевірений .. Тепер Є дві речі: 1. якщо ви вважаєте, що я невірний з моїм розумінням, будь ласка, виправте мене або 2. якщо ви вважаєте, що моя посада не має значення, я радий видалити те саме. з повагою, Atul
atul sachan

0

Якщо ви хочете опрацювати виняток у потоці та продовжувати обробку додаткових, у DZone є чудова стаття Брайана Вермеера, що використовує концепцію Either . Це показує чудовий спосіб вирішення цієї ситуації. Єдине, чого не вистачає - зразок коду. Це зразок мого дослідження з використанням понять із цієї статті.

@Test
public void whenValuePrinted_thenPrintValue() {

    List<Integer> intStream = Arrays.asList(0, 1, 2, 3, 4, 5, 6);
    intStream.stream().map(Either.liftWithValue(item -> doSomething(item)))
             .map(item -> item.isLeft() ? item.getLeft() : item.getRight())
             .flatMap(o -> {
                 System.out.println(o);
                 return o.isPresent() ? Stream.of(o.get()) : Stream.empty();
             })
             .forEach(System.out::println);
}

private Object doSomething(Integer item) throws Exception {

    if (item == 0) {
        throw new Exception("Zero ain't a number!");
    } else if (item == 4) {
        return Optional.empty();
    }

    return item;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.