Core Java

noException in stream operation

This article is about some simple coding practice. Nothing really fancy. It is also discussed on StackOverflow.

You just refactored a huge and complex loop to a more readable stream expression forgetting that some of the method calls throw exception. The method containing this code throws this exception, it is declared in the method head. You do not want to deal with this exception on this level. It is cared about on higher levels of the call stack. And you get that annoying error in the code like a splinter under the nail.

Say you want to convert strings to IP addresses.

private static final String[] allowed = {"127.0.0.1", "::1"};

...

Arrays.stream(allowed)
      .map(InetAddress::getByName)
      .collect(Collectors.toSet());

The problem is that getByName(String host) throws UnknownHostException. This is not a RuntimeException so it has to be checked but the method map() needs a Function as an argument and Function does not throw any exception. We need a version of getByName that does not throw exception (or we need to use a different language that is more lame with exceptions).

Arrays.stream(allowed)
       .map(s -> {
                   try {
                     return InetAddress.getByName(s);
                     } catch (UnknownHostException e) {
                     throw new RuntimeException(e);
                     }
                 }).collect(Collectors.toSet());

This is just more ugly and messier than the original loop was. Could this try/catch whatever thing be put into a utility class and call some lame static method that wraps the actual call? Kind of yes. Import the following method statically:

public interface ExceptionalSupplier<T> {
        T apply() throws Exception;
    }
...
    public static <T> T lame(ExceptionalSupplier<T> z) {
        try {
            return z.apply();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

After the import you can write

Arrays.stream(allowed)
      .map(s -> lame(() -> InetAddress.getByName(s)))
      .collect(Collectors.toSet());

the catch is that you can not just lame( ... ) the call. You have to convert it to an exceptional supplier. A functional interface that has the same look-alike as Supplier but it allows exceptions.

Still not ideal. (Well, it is Java, so what did you expect?) Okay. It is Java, but it still can be made better. What if instead of converting the expression through a supplier to an expression that is not throwing the exception we could convert the “Function” that throws the exception into one that is not throwing the exception. We need a method that accepts an exceptional function and returns a normal function. That way we can save the () -> noise in our code. Readability rulez.

public interface ExceptionalFunction<T, R> {
        R apply(T r) throws Exception;
    }
...
    public static <T, R> Function<T, R> lame(ExceptionalFunction<T, R> f) {
        return (T r) -> {
            try {
                return f.apply(r);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }

With that utility the “final” expression will be

Collection<InetAddress> allowedAddresses =
        Arrays.stream(allowed)
              .map(lame(InetAddress::getByName))
              .collect(Collectors.toSet());

The actual utility class in the GIST defines a WrapperException extending RuntimeException so that you can catch the exception somewhere in the method, like

public myMethod() throws IOException {
try{
    ... do whatever here we do ...
   } catch (RuntTimeExceptionWrapper.WrapperException we) {
       throw (IOException) we.getCause();
   }

That way the method will throw the exception but if anywhere there is another RuntimeException that will be throwing up uncaught.

This is just a simple, nice and little trick that helps you keep up with Java, which is backward compatible instead of starting development with some other language that is modern, clutter-free and let’s you focus more on the functionality you need to code instead of coding techniques.

Reference: noException in stream operation from our JCG partner Peter Verhas at the Java Deep blog.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button