DEV Community

Roman Khlebnov
Roman Khlebnov

Posted on

Dealing with checked exceptions in Java Stream API — easier.

Have you ever had to deal with some APIs that throw checked exceptions within Java Stream API?

Your answer will most likely be “yes” and that resulted in the code like this:

import java.util.ArrayList;
import java.util.List;

public class Demo {
    public static String throwing(String source) throws Exception {
        throw new Exception(source);
    }

    public static void main(String[] args) {
        List<String> test = new ArrayList<>();
        test.add("sample");

        try {
           test
             .stream()
             .map(s -> {
                 try {
                    return throwing(s);
                 } catch (Exception e) {
                    // Here we would have to:
                    // a) Return some value to filter out, log
                    // b) Wrap and rethrow an exception
                    throw new RuntimeException(e);
                }
             })
            .forEach(s -> {
                try {
                    throwing(s);
                } catch (Exception e) {
                    // Here we would have to:
                    // a) Suppress and log exception
                    // b) Wrap and rethrow an exception
                   throw new RuntimeException(e);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

If you had this happening, you probably had to improvise a bit.


Me too. With this in mind I have made a small, open-source, dependency-less library to help myself and others to deal with this problem like this:

import io.github.suppierk.java.util.function.*;

import java.util.ArrayList;
import java.util.List;

public class Demo {
    public static String throwing(String source) throws Exception {
        throw new Exception(source);
    }

    public static void main(String[] args) {
        List<String> test = new ArrayList<>();
        test.add("sample");

        try {
           test
             .stream()
             .map((ThrowableFunction<String, String>) Demo::throwing)
             .forEach((ThrowableConsumer<String>) Demo::throwing);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

or even more concisely like this:

import io.github.suppierk.java.util.function.*;

import java.util.ArrayList;
import java.util.List;

public class Demo {
    public static ThrowableFunction<String, String> throwingMap() {
        return source -> {
            throw new Exception(source);
        };
    }

    public static ThrowableConsumer<String> throwingConsumer() {
        return source -> {
            throw new Exception(source);
        };
    }

    public static void main(String[] args) {
        List<String> test = new ArrayList<>();
        test.add("sample");

        try {
           test
             .stream()
             .map(Demo.throwingMap())
             .forEach(Demo.throwingConsumer());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Well, this is neat - but what if I do want to process a bunch of elements and deal with exception later?

For this purpose I have added Try interface to the library - it acts like Optional, but holds either value itself or exception - this is quite commonly used in functional programming, giving you ability to write code like this:

import io.github.suppierk.java.util.Try;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class Test {
    public String throwingMethod(String source) throws Exception {
        throw new Exception(source);
    }

    public void processingMethod() {
        List<String> test = new ArrayList<>();

        test.stream()
                .map(s -> Try.of(() -> throwingMethod(s)))
                .filter(Try::isSuccess)
                .collect(Collectors.toList());
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

There are much more powerful libraries, like Vavr, there are similar pieces of this functionality scattered or built into other libraries as well (JUnit Assertions.assertThrows) — however my goal was to make what seemed as something that had to be in Java Stream API from the beginning in a way that it will not raise any concerns: there are no external dependencies, there are no sophisticated classes, just plain old Java.

The library itself is Java 8 compatible — I do hope that it will help you to reduce boilerplate code as well and it is available in Maven Central under:

<dependency>
    <groupId>io.github.suppierk</groupId>
    <artifactId>java-throwable-utils</artifactId>
    <version>1.0.2</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Top comments (0)