Java 8: Lambda-Streams, Filter by Method with Exception
Asked Answered
C

15

210

I have a problem trying out the Lambda expressions of Java 8. Usually it works fine, but now I have methods that throw IOException's. It's best if you look at the following code:

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

The problem is, it doesn't compile, because I have to catch the possible exceptions of the isActive- and the getNumber-Methods. But even if I explicitly use a try-catch-Block like below, it still doesn't compile because I don't catch the Exception. So either there is a bug in JDK, or I don't know how to catch these Exceptions.

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){
        }
    }
    ....
}

How can I get it work? Can someone hint me to the right solution?

Chanell answered 3/11, 2013 at 19:51 Comment(4)
Related: #18198676Prothalamion
Related: #31638392Civic
The simple and correct answer: catch the exception inside the lambda.Resplendence
I am not sure but if Account object is domain in such application like Bank then why we would throw IoException on the domain method. In an ideal scenario we would be having some service where we might be fetching the account and then active and other state for that domain would be already there and we can just filter or do operation on those.Milo
C
239

You must catch the exception before it escapes the lambda:

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

Consider the fact that the lambda isn't evaluated at the place you write it, but at some completely unrelated place, within a JDK class. So that would be the point where that checked exception would be thrown, and at that place it isn't declared.

You can deal with it by using a wrapper of your lambda that translates checked exceptions to unchecked ones:

public static <T> T uncheckCall(Callable<T> callable) {
    try {
        return callable.call();
    } catch (RuntimeException e) {
        throw e;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Your example would be written as

return s.filter(a -> uncheckCall(a::isActive))
        .map(Account::getNumber)
        .collect(toSet());

In my projects I deal with this issue without wrapping; instead I use a method which effectively defuses compiler's checking of exceptions. Needless to say, this should be handled with care and everybody on the project must be aware that a checked exception may appear where it is not declared. This is the plumbing code:

public static <T> T uncheckCall(Callable<T> callable) {
    try {
        return callable.call();
    } catch (Exception e) {
        sneakyThrow(e);
        return null; // Unreachable but needed to satisfy compiler
    }
}

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

and you can expect to get an IOException thrown in your face, even though collect does not declare it. In most, but not all real-life cases you would want to just rethrow the exception, anyway, and handle it as a generic failure. In all those cases, nothing is lost in clarity or correctness. Just beware of those other cases, where you would actually want to react to the exception on the spot. The developer will not be made aware by the compiler that there is an IOException to catch there and the compiler will in fact complain if you try to catch it because we have fooled it into believing that no such exception can be thrown.

Civic answered 3/11, 2013 at 20:4 Comment(22)
I've seen NettyIO do the "sneaky throw" before, and I wanted to throw my chair out the window. "What? Where did that checked exception leak from?" This is the first legit use case for sneaky throw I have seen yet. As a programmer you have to be vigilant about indicating sneaky throw is possible. Maybe better is just to create another stream interface/impl that supports checked exception?Calebcaledonia
No sane API should deliver undeclared checked exceptions to the client, that's for sure. Within the API there can be an understanding, though, that checked exception can leak. They don't cause harm as long as they are just another sign of general failure and are about to be caught and handled wholesale.Civic
@Calebcaledonia This is the exact reason why sneaky throws are a bad idea. Shortcircuiting the compiler is bound to confuse future maintainers.Bannockburn
@ThorbjørnRavnAndersen Checked exceptions are another bad idea, especially within lambda bodies. If they weren't, nobody would ever consider the sneaky throw.Civic
Just because you don't like the rules, doesn't mean its a good idea to take the law into your own hands. Your advice is irresponsible because it places the convenience of the code writer over the far more important considerations of transparency and maintainability of the program.Resplendence
@brian Just because something is a rule doesn't mean it's a good idea. But I'm surprised that you refer to the second part of my answer as "advice" since I thought I made it patently obvious what I propose as a solution, and what I offer as an FYI to the interested reader, with profuse disclaimers.Civic
Would you suggest to use the lambda-expression with a try-catch-block when iterating over a java-stream with forEach instead of an enhanced-for-loop? Im updating one of my older projects to Java 8. The lambda-expressions came in handy for a long time, but there is a part in the project that handles exception at a high level, meaning everything keeps passing the thrown Exceptions. Are the pros of lambda-expressions overwhelming the cons of the constrained way to handle Exceptions within those expression?Keloid
I find your try-catch lambda notation useful, although there is a small syntax error there, the correct form looks like this: s = s.filter(a -> { try { return a.isActive(); } catch (IOException e) { throw new UncheckedIOException(e); } } );Cyrstalcyrus
Note, that class is called Utils, if you wonder about Utils.<RuntimeException, T>sneakyThrow0(e);Katlin
@Katlin The class in this answer is called Util, not Utils, but the name has no particular significance.Civic
There is a library that provides these and some more helpers: github.com/zalando/faux-pasPolymerization
@Polymerization Faux Pas uses an approach that has been around for a long time and its weakness is that it must special-case each functional interface individually. In my approach, all you need is just two functional interfaces to un-check any functional interface.Civic
You need to add return false; to the catch condition. Otherwise there's an error.Intumescence
@geneb. Which of all the catch conditions in the answer, and what's the error?Civic
The very first code snippet. You have return a.isActive(); in the Try block. But you also need an explicit return in the Catch block.Intumescence
@geneb. Code after the throw statement is unreachable so actually if I added it, then I'd have a compiler error.Civic
The sneakyThrow method can be found in apache.commons.lang3.exception.ExceptionUtils under the name typeErasure. Use it if you can so that you don't have to be the committer that added sneakyThrow to your project. ;-)Ctenophore
The misguided design decision of Checked Exceptions should have been mitigated by javac supporting a compile-scope attribute and functionality like the Project Lombok @SneakyThrows attribute, but with support for more scopes like a single statement, including a chained stream sequence.Permanence
private static <T extends Throwable> void sneakyThrow(Throwable t) throws T - From this method signature, how does Java infers the value of T, i.e., exception class that will be re-thrown?Dilate
@Dilate Lacking any other constraints, it will infer it to RuntimeException. The point is that you can throw something that isn't a RuntimeException from this because no actual cast is made with (T) t, and the method doesn't declare a checked exception.Civic
How could one modify uncheckCall to also accept void return type statements? I cannot seem to get it to work for both <T> T> return type and void.Iver
@Iver The inability to do that is the reason the code in the answer declares uncheckRun and RunnableExc.Civic
D
34

You can also propagate your static pain with lambdas, so the whole thing looks readable:

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

propagate here receives java.util.concurrent.Callable as a parameter and converts any exception caught during the call into RuntimeException. There is a similar conversion method Throwables#propagate(Throwable) in Guava.

This method seems being essential for lambda method chaining, so I hope one day it will be added to one of the popular libs or this propagating behavior would be by default.

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);
    }
}
Devastate answered 3/11, 2013 at 23:47 Comment(2)
Also, if you choose to use propagate method from Guava, then take into account that it is deprecated. Use throwIfUnchecked instead.Arium
There is a good reason why Guava deprecated propagate: github.com/google/guava/wiki/…Lecce
T
26

This UtilException helper class lets you use any checked exceptions in Java streams, like this:

Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
      .map(rethrowFunction(Class::forName))
      .collect(Collectors.toList());

Note Class::forName throws ClassNotFoundException, which is checked. The stream itself also throws ClassNotFoundException, and NOT some wrapping unchecked exception.

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

}

Many other examples on how to use it (after statically importing 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");
    }

But don't use it before understanding the following advantages, disadvantages, and limitations:

• If the calling-code is to handle the checked exception you MUST add it to the throws clause of the method that contains the stream. The compiler will not force you to add it anymore, so it's easier to forget it.

• If the calling-code already handles the checked exception, the compiler WILL remind you to add the throws clause to the method declaration that contains the stream (if you don't it will say: Exception is never thrown in body of corresponding try statement).

• In any case, you won't be able to surround the stream itself to catch the checked exception INSIDE the method that contains the stream (if you try, the compiler will say: Exception is never thrown in body of corresponding try statement).

• If you are calling a method which literally can never throw the exception that it declares, then you should not include the throws clause. For example: new String(byteArr, "UTF-8") throws UnsupportedEncodingException, but UTF-8 is guaranteed by the Java spec to always be present. Here, the throws declaration is a nuisance and any solution to silence it with minimal boilerplate is welcome.

• If you hate checked exceptions and feel they should never be added to the Java language to begin with (a growing number of people think this way, and I am NOT one of them), then just don't add the checked exception to the throws clause of the method that contains the stream. The checked exception will, then, behave just like an UNchecked exception.

• If you are implementing a strict interface where you don't have the option for adding a throws declaration, and yet throwing an exception is entirely appropriate, then wrapping an exception just to gain the privilege of throwing it results in a stacktrace with spurious exceptions which contribute no information about what actually went wrong. A good example is Runnable.run(), which does not throw any checked exceptions. In this case, you may decide not to add the checked exception to the throws clause of the method that contains the stream.

• In any case, if you decide NOT to add (or forget to add) the checked exception to the throws clause of the method that contains the stream, be aware of these 2 consequences of throwing CHECKED exceptions:

1) The calling-code won't be able to catch it by name (if you try, the compiler will say: Exception is never thrown in body of corresponding try statement). It will bubble and probably be catched in the main program loop by some "catch Exception" or "catch Throwable", which may be what you want anyway.

2) It violates the principle of least surprise: it will no longer be enough to catch RuntimeException to be able to guarantee catching all possible exceptions. For this reason, I believe this should not be done in framework code, but only in business code that you completely control.

In conclusion: I believe the limitations here are not serious, and the UtilException class may be used without fear. However, it's up to you!

Tagmeme answered 26/12, 2014 at 20:15 Comment(0)
O
13

You can potentially roll your own Stream variant by wrapping your lambda to throw an unchecked exception and then later unwrapping that unchecked exception on terminal operations:

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

Then you could use this the same exact way as a 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());

This solution would require quite a bit of boilerplate, so I suggest you take a look at the library I already made which does exactly what I have described here for the entire Stream class (and more!).

Octodecillion answered 20/4, 2014 at 23:15 Comment(2)
Hello... tiny bug? new StreamBridge<>(ts, IOException.class); -> new StreamBridge<>(s, IOException.class);Calebcaledonia
@Calebcaledonia Yep. It also should have said StreamAdapter.Octodecillion
R
6

TLDR: Try to avoid the issue by refactoring your code: Split "error-prone" operations from "safe" operations and just use the safe ones in lambdas.


Details:

This does not directly answer the question (there are many other answers that do) but tries to avoid the problem in the first place:

In my experience the need to handle exceptions in a Stream (or other lambda expression) often comes from the fact that the exceptions are declared to be thrown from methods where they should not be thrown. This often comes from mixing business logic with in- and output. Your Account interface is a perfect example:

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

Instead of throwing an IOException on each getter, consider this design:

interface AccountReader {
    Account readAccount(…) throws IOException;
}

interface Account {
    boolean isActive();
    String getNumber();
}

The method AccountReader.readAccount(…) could read an account from a database or a file or whatever and throw an exception if it does not succeed. It constructs an Account object that already contains all values, ready to be used. As the values have already been loaded by readAccount(…), the getters would not throw an exception. Thus you can use them freely in lambdas without the need of wrapping, masking or hiding the exceptions.

Note that you still need to handle the exception thrown by readAccount(…). After all, that is why exceptions exist in the first place. But the assumption is that readAccount(…) is used "somewhere else", i.e. outside of lambdas, where you can use the "normal" exception handling mechanisms Java provides, i.e. try-catch to handle it or throws to let it "bubble up".

Of course it is not always possible to do it the way I described, but often it is and it leads to cleaner code altogether (IMHO):

  • Better separation of concerns and following single responsibility principle
  • Less boilerplate: You don't have to clutter your code with throws IOException for no use but to satisfy the compiler
  • Error handling: You handle the errors where they happen - when reading from a file or database - instead of somewhere in the middle of your business logic only because you want to get a fields value
  • You may be able to make Account immutable and profit from the advantages thereof (e.g. thread safety)
  • You don't need "dirty tricks" or workarounds to use Account in lambdas (e.g. in a Stream)
Respecting answered 23/4, 2016 at 21:36 Comment(3)
So you mean you handle the database or file exception in readAccount() and wrap it into IOException? But IOException is still checked exception and it will have to be caught in the caller if it wants to construct a stream right?Entero
@WesternGun, if you need to wrap the exception in readAccount() or just let it "bubble up" without wrapping depends on your case. You can also throw a SQLException there or whatever. The important thing is, only readAccount() may fail. The methods in Account don't. And yes, the caller of readAccount() needs to handle the exception some way. But using the Account within a stream is easy, because it never throws something and that was the point of the question. TLDR: Split "error-prone" operations from "safe" operations and just use the safe ones in lambdas.Respecting
@WesternGun, see my last edit to the answer :-)Respecting
G
5

Use #propagate() method. Sample non-Guava implementation from Java 8 Blog by Sam Beran:

public class Throwables {
    public interface ExceptionWrapper<E> {
        E wrap(Exception e);
    }

    public static <T> T propagate(Callable<T> callable) throws RuntimeException {
        return propagate(callable, RuntimeException::new);
    }

    public static <T, E extends Throwable> T propagate(Callable<T> callable, ExceptionWrapper<E> wrapper) throws E {
        try {
            return callable.call();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw wrapper.wrap(e);
        }
    }
}
Galvanotropism answered 2/7, 2014 at 19:50 Comment(1)
The Java 8 blog link is dead.Esthete
R
4

Extending @marcg solution, you can normally throw and catch a checked exception in Streams; that is, compiler will ask you to catch/re-throw as is you were outside 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;
}

Then, your example would be written as follows (adding tests to show it more clearly):

@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
    }
}
Rolo answered 26/6, 2015 at 11:0 Comment(2)
This example doesn't compileAquacade
Hi @RomanM thanks for pointing this: i've corrected the missing return type on method "throwActualException". We're using this in production so I hope it's working on your side too.Rolo
D
4

It can be resolved by below simple code with Stream and Try in abacus-common:

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

Or:

Stream.of(accounts)
      .filterE(Account::isActive)
      .mapE(Account::getNumber)
      .toSet();

Disclosure: I'm the developer of abacus-common.

Dysphemism answered 2/12, 2016 at 1:45 Comment(0)
M
2

To properly add IOException (to RuntimeException) handling code, your method will look like this:

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

The problem now is that the IOException will have to be captured as a RuntimeException and converted back to an IOException -- and that will add even more code to the above method.

Why use Stream when it can be done just like this -- and the method throws IOException so no extra code is needed for that too:

Set<String> set = new HashSet<>();
for(Account a: accounts.values()){
  if(a.isActive()){
     set.add(a.getNumber());
  } 
}
return set;
Mckenzie answered 4/11, 2013 at 2:10 Comment(0)
F
1

Keeping this issue in mind I developed a small library for dealing with checked exceptions and lambdas. Custom adapters allow you to integrate with existing functional types:

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

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

Frogmouth answered 19/2, 2016 at 18:22 Comment(0)
B
1

Your example can be written as:

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

The Unthrow class can be taken here https://github.com/SeregaLBN/StreamUnthrower

Burkholder answered 4/3, 2016 at 13:18 Comment(0)
D
1

It's also possible to use some external (to stream) error indicator for throwing exception at higher level:

List<String> errorMessages = new ArrayList<>(); // error indicator
//..
errorMessages.clear();

List<String> names = new ArrayList<>(Arrays.asList("andrey", "angela", "pamela"));

names.stream()
.map(name -> {
    if (name != "pamela") {
      errorMessages.add(name + " is wrong here!"); 
      return null; // triggering the indicator
    }
    return name;
} )
.filter(elem -> (elem != null)) // bypassing propagation of only current unwanted data
//.filter(elem -> (errorMessages.size() == 0)) // or blocking any propagation once unwanted data detected
.forEach(System.out::println);

if (errorMessages.size() > 0) { // handling the indicator
  throw  new RuntimeException(String,join(", ", errorMessages));
}
Dronski answered 6/9, 2021 at 10:55 Comment(0)
P
0

If you don't mind using 3rd party libraries, AOL's cyclops-react lib, disclosure::I am a contributor, has a ExceptionSoftener class that can help here.

 s.filter(softenPredicate(a->a.isActive()));
Pontius answered 24/2, 2016 at 17:36 Comment(0)
C
0

The functional interfaces in Java don’t declare any checked or unchecked exception. We need to change the signature of the methods from:

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

To:

boolean isActive();
String getNumber();

Or handle it with try-catch block:

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

Another option is to write a custom wrapper or use a library like ThrowingFunction. With the library we only need to add the dependency to our pom.xml:

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

And use the specific classes like ThrowingFunction, ThrowingConsumer, ThrowingPredicate, ThrowingRunnable, ThrowingSupplier.

At the end the code looks like this:

public Set<String> getActiveAccountNumbers() {
  return accounts.values().stream()
    .filter(ThrowingPredicate.unchecked(Account::isActive))
    .map(ThrowingFunction.unchecked(Account::getNumber))
    .collect(Collectors.toSet());
}
Coprology answered 30/11, 2018 at 19:8 Comment(0)
C
0

If you want to handled the exception within the stream and continue processing the additional ones, there is an excellent article in DZone by Brian Vermeer using the concept of an Either. It shows an excellent way of handling this situation. The only thing missing is sample code. This is a sample of my exploration using the concepts from that article.

@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;
}
Calculate answered 26/9, 2019 at 16:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.