4

Lets say you have a 3rd party library which exposes the next interface.

interface Mapper {

    String doMap(String input) throws CheckedException;

}

class CheckedException extends Exception {

}

I am aware checked exception are generally a bad practice in Java, but this code comes from a 3rd party and I can't modify it.

I want to use an implementation of the Mapper interface in combination with the Java8 streams API. Consider the sample implementation below.

class MapperImpl implements Mapper {

    public String doMap(String input) throws CheckedException {
        return input;
    }

}

Now, I would like to apply the mapper to a collection of strings, for example.

public static void main(String[] args) {
    List<String> strings = Arrays.asList("foo", "bar", "baz");
    Mapper mapper = new MapperImpl();

    List<String> mappedStrings = strings
            .stream()
            .map(mapper::doMap)
            .collect(Collectors.toList());
}

The code fails to compile, since Function does not know how to handle the CheckedException declared by doMap. I came up with two possible solutions.

Solution #1 - wrap invocation

.map(value -> {
                try {
                    return mapper.doMap(value);
                } catch (CheckedException e) {
                    throw new UncheckedException();
                }
            })

Solution #2 - write an utility method

public static final String uncheck (Mapper mapper, String input){
    try {
        return mapper.doMap(input);
    } catch (CheckedException e){
        throw new UncheckedException();
    }
}

And then I can use

.map(value -> Utils.uncheck(mapper, value))

Which is, in your opinion, the best approach to deal with checked exceptions in the context of Java8 streams (and in the broader context of lambda expressions)?

Thanks!

  • 1
    Both solutions are essentially the same thing. The lambda (Solution 1) creates an instance of ``java.util.function`` that does the try/catching, Solution 2 delegates that work to the named function ``Utils.uncheck``. Since you are forced to catch the Exception, there's no useful way around this. – f1sh Sep 17 '18 at 11:46
  • 1
    Does *your* mapper actually ever throw anything? Or is the mapper itself from the lib as well? Right now you could just remove the `throws CheckedException` from the implementing class and no longer reference the interface but the mapper class itself. – luk2302 Sep 17 '18 at 11:49
  • @luk2302 Yes, the mapper implementation is part of the lib as well. –  Sep 17 '18 at 11:56

2 Answers2

3

You've basically listed two viable options.

One more option is to make the checked exceptions be thrown out of the stream processing function ("propagate" or "sneak" a checked exception). This is done by catching a checked exception and re-throwing it as a RuntimeException (by casting). Take a look at this great answer for details.

Multiple libraries have been developed to deal with exception handling in stream API. For example, you may take a look at the NoException library: https://noexception.machinezoo.com/

It gives you a convenient way to wrap / sneak / log / ignore checked or unchecked exceptions.

For example, in your case it would be:

.map(value -> Exceptions.sneak().function(mapper::doMap))

or

.map(value -> Exceptions.wrap().function(mapper::doMap))

P.S.: I'm NOT the author of the library, nor a contributor, but I've used this library in several projects.

Alex Shesterov
  • 26,085
  • 12
  • 82
  • 103
1

You can look at the library faux-pas that simplifies error handling for Functional Programming in Java. I think it's great to manage check exception in stream.


P.S.: I'm NOT the author of the library, nor a contributor, but I've used this library in several projects.

Matthieu Gabin
  • 830
  • 9
  • 26