Java 8:Lambda-Streams,按方法进行过滤(异常)


168

我在尝试Java 8的Lambda表达式时遇到问题。通常它可以正常工作,但是现在我有抛出IOException的方法。最好查看以下代码:

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

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

问题是,它无法编译,因为我必须捕获isActive-和getNumber-Methods的可能异常。但是,即使我显式使用了如下所示的try-catch-Block,它仍然无法编译,因为我没有捕获到Exception。因此,或者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
简单正确的答案:在lambda中捕获异常。
Brian Goetz 2015年

Answers:


211

您必须先捕获异常,然后才能逃逸出lambda:

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

考虑以下事实:在JDK类中,lambda不在您编写的位置评估,而是在一些完全不相关的位置评估。这样便可以抛出该检查异常,并且在该位置不声明该异常。

您可以通过使用lambda的包装来处理它,该包装将已检查的异常转换为未检查的异常:

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 可以理解,检查异常可能会泄漏。只要它们只是普遍失败的另一个迹象,并且不会被大量捕捞和处理,它们就不会造成伤害。
Marko Topolnik

5
@kevinarpe这就是为什么偷偷摸摸的抛出是个坏主意的确切原因。缩短编译器势必会使未来的维护人员感到困惑。
托尔比约恩Ravn的安徒生

29
仅仅因为您不喜欢规则,并不意味着将法律掌握在自己手中是个好主意。您的建议是不负责任的,因为它将代码编写者的便利放在程序透明度和可维护性的更重要的考虑上。
Brian Goetz 2015年

34
@brian仅因为某事是规则并不意味着它是一个好主意。但是令您惊讶的是,您将答案的第二部分称为“建议”,因为我认为我明确提出了我打算作为解决方案提出的内容,以及我作为FYI向感兴趣的读者提供的内容,并带有大量免责声明。
Marko Topolnik

29

您还可以使用lambda传播您的静态疼痛,因此整个过程看起来很可读:

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

propagate在这里接收java.util.concurrent.Callable作为参数,并将在调用期间捕获的任何异常转换为RuntimeException。番石榴中有类似的转换方法Throwables#propagate(Throwable)

该方法似乎对于lambda方法链至关重要,因此我希望有一天它会被添加到一个流行的库中,或者默认情况下,这种传播行为将是默认的。

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

但是在理解以下优点,缺点和限制之前,请不要使用它

•如果调用代码要处理检查的异常,则必须将其添加到包含流的方法的throws子句中。编译器不会再强迫您添加它,因此更容易忘记它。

•如果调用代码已经处理了检查到的异常,则编译器将提醒您将throws子句添加到包含流的方法声明中(如果不这样做,则会说:永远不会在相应的try语句的正文中引发异常) )。

•无论如何,您将无法包围流本身以捕获包含流的方法在内的已检查异常(如果尝试,编译器会说:永远不会在相应的try语句的主体中引发异常)。

•如果调用的方法从字面上讲永远不会引发它声明的异常,则不应包括throws子句。例如:new String(byteArr,“ UTF-8”)抛出UnsupportedEncodingException,但是Java规范保证UTF-8始终存在。在这里,throws声明是很麻烦的,欢迎采用最小化样板来使其静音的任何解决方案。

•如果您讨厌检查异常,并且觉得绝不应该将它们添加到Java语言中(越来越多的人以这种方式,但我不是其中之一),那么就不要将检查异常添加到Java语言中。包含流的方法的throws子句。然后,已检查的异常的行为就像未检查的异常。

•如果实现的是严格的接口,其中没有添加throws声明的选项,但是抛出异常是完全适当的,那么包装异常只是为了获得抛出它的特权,就会导致带有虚假异常的stacktrace不会提供有关实际出了什么问题的信息。一个很好的例子是Runnable.run(),它不会引发任何检查的异常。在这种情况下,您可以决定不将检查的异常添加到包含流的方法的throws子句中。

•无论如何,如果您决定不向包含流的方法的throws子句中添加(或忘记添加)已检查的异常,请注意引发CHECKED异常的以下两种后果:

1)调用代码将无法按名称捕获它(如果尝试,编译器会说:永远不会在相应的try语句的主体中引发异常)。它会冒泡,并且可能在主程序循环中被某些“ catch Exception”或“ catch Throwable”捕获,无论如何,这可能是您想要的。

2)它违反了最小惊讶原则:捕获RuntimeException不再足以保证捕获所有可能的异常。因此,我认为这不应在框架代码中完成,而应在完全控制的业务代码中完成。

结论:我相信这里的局限性并不严重,使用UtilException该类时无需担心。但是,这取决于您!


8

您可以Stream通过包装lambda引发未检查的异常,然后在终端操作中解包装该未检查的异常来滚动自己的变量:

@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
杰弗里2015年

5

使用#propagate()方法。Sam Beran的Java 8 Blog中的非Guava实现示例:

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博客链接已死。
Spycho 2015年

4

这不会直接回答问题(还有许多其他答案),但首先会避免出现此问题:

以我的经验,在一个Stream(或其他lambda表达式)中处理异常的需要通常来自以下事实:将异常声明为从不应抛出的方法中抛出。这通常是由于业务逻辑与输入和输出的混合。您的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(…),因此getter不会引发异常。因此,您可以在lambda中自由使用它们,而无需包装,掩盖或隐藏异常。

当然,并非总是可以按照我所描述的方式来做,但是通常是这样,这会导致代码更整洁(IMHO):

  • 更好地分离关注点并遵循单一责任原则
  • 样板更少:您不必throws IOException为了使编译器满意而使代码杂乱无用
  • 错误处理:处理错误发生的位置-从文件或数据库中读取时-而不是仅在业务逻辑中间的某个位置,仅是因为您想要获取字段值
  • 您可以使Account 不可变并从中受益(例如线程安全)
  • 您不需要Account在lambda中使用“肮脏的把戏”或变通方法(例如在中Stream

4

可以通过StreamTry in 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
    }
}

此示例无法编译
Roman M

@RomanM,您好:感谢您指出了这一点:我已经纠正了方法“ throwActualException”上缺少的返回类型。我们正在生产中使用它,因此我希望它也能在您的身边工作。
PaoloC '16

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

您的示例可以写成:

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

或使用try-catch块处理它:

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

我看不到任何在stream(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.如果您认为我的帖子无关紧要,我很高兴将其删除。
尊敬的

0

如果要处理流中的异常并继续处理其他异常,则Brian Vermeer 在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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.