Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Exception Handling in Java Streams

DZone's Guide to

Exception Handling in Java Streams

When you want to use a method that throws a checkedException, you have to do something extra if you want to call it in a lambda.

· Java Zone ·
Free Resource

Secure your Java app or API service quickly and easily with Okta's user authentication and authorization libraries. Developer accounts are free forever. Try Okta Instead.

The Stream API and lambda’s where a big improvement in Java since version 8. From that point on, we could work with a more functional syntax style. Now, after a few years of working with these code constructions, one of the bigger issues that remain is how to deal with checked exceptions inside a lambda.

As you all probably know, it is not possible to call a method that throws a checked exception from a lambda directly. In some way, we need to catch the exception to make the code compile. Naturally, we can do a simple try-catch inside the lambda and wrap the exception into a RuntimeException, as shown in the first example, but I think we can all agree that this is not the best way to go.

myList.stream()
  .map(item -> {
    try {
      return doSomething(item);
    } catch (MyException e) {
      throw new RuntimeException(e);
    }
  })
  .forEach(System.out::println);


Most of us are aware that block lambdas are clunky and less readable. They should be avoided as much as possible, in my opinion. If we need to do more than a single line, we can extract the function body into a separate method and simply call the new method. A better and more readable way to solve this problem is to wrap the call in a plain old method that does the try-catch and call that method from within your lambda.

myList.stream()
  .map(this::trySomething)
  .forEach(System.out::println);


private Item trySomething(Item item) {
  try {
    return doSomething(item);
  } catch (MyException e) {
    throw new RuntimeException(e);
  }
}


This solution is at least a bit more readable and we do separate our concerns. If you really want to catch the exception and do something specific and not simply wrap the exception into a RuntimeException, this can be a possible and readable solution for you.

RuntimeException

In many cases, you will see that people use these kinds of solutions to repack the exception into a RuntimeException or a more specific implementation of an unchecked Exception. By doing so, the method can be called inside a lambda and be used in higher order functions.

I can relate a bit to this practice because I personally do not see much value in checked exceptions in general, but that is a whole other discussion that I am not going to start here. If you want to wrap every call in a lambda that has a checked into a RuntimeException, you will see that you repeat the same pattern. To avoid rewriting the same code over and over again, why not abstract it into a utility function? This way, you only have to write it once and call it every time you need it.

To do so, you first need to write your own version of the functional interface for a function. Only this time, you need to define that the function may throw an exception.

@FunctionalInterface
public interface CheckedFunction<T,R> {
    R apply(T t) throws Exception;
}


Now, you are ready to write your own general utility function that accepts a CheckedFunction as you just described in the interface.You can handle the try-catch in this utility function and wrap the original exception into a RuntimeException (or some other unchecked variant). I know that we now end up with an ugly block lambda here and you could abstract the body from this. Choose for yourself if that is worth the effort for this single utility.

public static <T,R> Function<T,R> wrap(CheckedFunction<T,R> checkedFunction) {
  return t -> {
    try {
      return checkedFunction.apply(t);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  };
}


With a simple static import, you can now wrap the lambda that may throw an exception with your brand new utility function. From this point on, everything will work again.

myList.stream()
       .map(wrap(item -> doSomething(item)))
       .forEach(System.out::println);


The only problem left is that when an exception occurs, the processing of the your stream stops immediately. If that is no problem for you, then go for it. I can imagine, however, that direct termination is not ideal in many situations.

Either

When working with streams, we probably don't want to stop processing the stream if an exception occurs. If your stream contains a very large amount of items that need to be processed, do you want that stream to terminate when for instance the second item throws an exception? Probably not.

Let's turn our way of thinking around. Why not consider "the exceptional situation" just as much as a possible result as we would for a "successful" result. Let's consider it both as data, continuing to process the stream, and decide afterward what to do with it. We can do that, but to make it possible, we need to introduce a new type — the Either type.

The Either type is a common type in functional languages and not (yet) part of Java. Similar to the Optional type in Java, an Either is a generic wrapper with two possibilities. It can either be a Left or a Right but never both. Both left and right can be of any types. For instance, if we have an Either value, this value can either hold something of type String or of type Integer, Either<String,Integer>.

If we use this principle for exception handling, we can say that our Either  type holds either an  Exception or a value. By convenience, normally, the left is the Exception and the right is the successful value. You can remember this by thinking of the right as not only the right-hand side but also as a synonym for “good,” “ok,” etc.

Below, you will see a basic implementation of the Either type. In this case, I used the  Optional type when we try to get the left or the right because we:

public class Either<L, R> {

    private final L left;
    private final R right;

    private Either(L left, R right) {
        this.left = left;
        this.right = right;
    }

    public static <L,R> Either<L,R> Left( L value) {
        return new Either(value, null);
    }

    public static <L,R> Either<L,R> Right( R value) {
        return new Either(null, value);
    }

    public Optional<L> getLeft() {
        return Optional.ofNullable(left);
    }

    public Optional<R> getRight() {
        return Optional.ofNullable(right);
    }

    public boolean isLeft() {
        return left != null;
    }

    public boolean isRight() {
        return right != null;
    }

    public <T> Optional<T> mapLeft(Function<? super L, T> mapper) {
        if (isLeft()) {
            return Optional.of(mapper.apply(left));
        }
        return Optional.empty();
    }

    public <T> Optional<T> mapRight(Function<? super R, T> mapper) {
        if (isRight()) {
            return Optional.of(mapper.apply(right));
        }
        return Optional.empty();
    }

    public String toString() {
        if (isLeft()) {
            return "Left(" + left +")";
        }
        return "Right(" + right +")";
    }
}


You can now make your own functions return an Either instead of throwing an  Exception. But that doesn't help you if you want to use existing methods that throw a checked  Exception inside a lambda right? Therefore, we have to add a tiny utility function to the Either type I described above.

public static <T,R> Function<T, Either> lift(CheckedFunction<T,R> function) {
  return t -> {
    try {
      return Either.Right(function.apply(t));
    } catch (Exception ex) {
      return Either.Left(ex);
    }
  };
}


By adding this static lift method to the Either, we can now simply "lift" a function that throws a checked exception and let it return an Either. If we take the original problem, we now end up with a Stream of Eithers instead of a possible RuntimeException that may blow up my entire Stream.

myList.stream()
       .map(Either.lift(item -> doSomething(item)))
       .forEach(System.out::println);


This simply means that we have taken back control. By using the filter function in the Stream APU, we can simply filter out the left instances and, for example, log them. You can also filter the right instances and simply ignore the exceptional cases. Either way, you are back in control again and your stream will not terminate instantly when a possible RuntimeException occurs.

Because Either is a generic wrapper, it can be used for any type, not just for exception handling. This gives us the opportunity to do more than just wrapping the Exception into the left part of an Either. The issue we now might have is that if the  Either only holds the wrapped exception, and we cannot do a retry because we lost the original value. By using the ability of the Either to hold anything, we can store both the exception and the value inside a left. To do so, we simply make a second static lift function like this.

public static <T,R> Function<T, Either> liftWithValue(CheckedFunction<T,R> function) {
  return t -> {
    try {
      return Either.Right(function.apply(t));
    } catch (Exception ex) {
      return Either.Left(Pair.of(ex,t));
    }
  };
}


You see that in this liftWithValue function within the Pair type is used to pair both the exception and the original value into the left of an Either.  Now, we have all the information we possibly need if something goes wrong, instead of only having the Exception.

The Pair type used here is another generic type that can be found in the Apache Commons lang library, or you can simply implement your own. Anyway, it is just a type that can hold two values.

public class Pair<F,S> {
    public final F fst;
    public final S snd;

    private Pair(F fst, S snd) {
        this.fst = fst;
        this.snd = snd;
    }

    public static <F,S> Pair<F,S> of(F fst, S snd) {
        return new Pair<>(fst,snd);
    }
}


With the use of the liftWithValue, you now have all the flexibility and control to use methods that may throw an  Exception inside a lambda. When the  Either is a right, we know that the function was applied correctly and we can extract the result. If, on the other hand, the Either is a left, we know something went wrong and we can extract both the Exception and the original value, so we can proceed as we like. By using the Either type instead of wrapping the checked  Exception into a  RuntimeException, we prevent the Stream from terminating halfway.

Try

People that may have worked with for instance Scala may use the  Try instead of the Either for exception handling. The  Try type is something that is very similar to the  Either type. It has, again, two cases: “success” or “failure.” The failure can only hold the type Exception, while the success can hold anything type you want. So, the Try is nothing more than a specific implementation of the  Either where the left type (the failure) is fixed to type Exception.

public class Try<Exception, R> {

    private final Exception failure;
    private final R succes;

    public Try(Exception failure, R succes) {
        this.failure = failure;
        this.succes = succes;
    }
}


Some people are convinced that it is easier to use, but I think that because we can only hold the Exception itself in the failure part, we have the same problem as explained in the first part of the Either section. I personally like the flexibility of the Either type more. Anyway, in both cases, if you use the Try or the Either, you solve the initial problem of exception handling and do not let your stream terminate because of a RuntimeException.

Libraries

Both the Either and the  Try are very easy to implement yourself. On the other hand, you can also take a look at functional libraries that are available. For instance, VAVR (formerly known as Javaslang) does have implementations for both types and helper functions available. I do advise you to take a look at it because it holds a lot more than only these two types. However, you have ask yourself the question of whether you want this large library as a dependency just for exception handling when you can implement it yourself with just a few lines of code.

Conclusion

When you want to use a method that throws a  checkedException, you have to do something extra if you want to call it in a lambda. Wrapping it into a RuntimeException can be a solution to make it work. If you prefer to use this method, I urge you to create a simple wrapper tool and reuse it, so you are not bothered by the try/catch every time.

If you want to have more control, you can use the Either or  Try types to wrap the outcome of the function, so you can handle it as a piece of data. The stream will not terminate when a RuntimeException is thrown and you have the liberty to handle the data inside your stream as you please.

Secure your Java app or API service quickly and easily with Okta's user authentication and authorization libraries. Developer accounts are free forever. Try Okta Instead.

Topics:
java ,stream api ,exception handling ,functional programing ,either ,Try ,exception ,RuntimeException

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}