July 29, 2018

Exceptions in Java 8 Streams and Lambda Expressions

Java 8 Lambda Expressions allow us to do functional programming, but sadly they don't deal with exceptions very well.

Handling Unchecked Exceptions
Let's say we have a list of integers and we are dividing a constant number (lets say 16) with every element of this list, we need to print result of this division.

List < Integer > myList = Arrays.asList(7, 9, 16, 25, 27);
myList.forEach(i - > System.out.println(16 / i));

The above Lambda expression will work fine until it encounter 0. If the 0 is present in the myList then it will throw ArithmeticException: / by zero.

List < Integer > myList = Arrays.asList(7, 9, 16, 25, 27);
myList.forEach(i - > {
  try {
    System.out.println(50 / i);
  } catch (ArithmeticException e) {
    System.err.println("Arithmetic Exception occured");
  }
});

This will work fine, but because of try catch our Lambda expression is longer than a small function. We can provide wrapper lambda to resolve this. In the below example, myWrapperLamda is responsible for exception handling.

List < Integer > myList = Arrays.asList(7, 9, 16, 25, 27);
myList.forEach(myWrapperLamda(i - > System.out.println(50 / i)));

static Consumer < Integer  > myWrapperLamda(Consumer < Integer > consumer) {
  return i - > {
    try {
      consumer.accept(i);
    } catch (ArithmeticException e) {
      System.err.println("Arithmetic Exception occured");
    }
  };
}

The wrapper method works fine but it doesn't reduce the actual number of lines of code being written.

We can make our wrapper a bit specific to a particular use case to handle multiple exceptions. The new wrapper method will take two arguments, the lambda expression and the type of Exception to be caught. This new lambda wrapper is capable of handling all data types, not just Integers, and catch any specific type of exception and not the superclass Exception. e.g:

static < T, E extends Exception > Consumer < T >
    consumerWrapper(Consumer < T > consumer, Class < E > clazz) {

  return i - > {
    try {
      consumer.accept(i);
    } catch (Exception ex) {
      try {
        E exCast = clazz.cast(ex);
        System.err.println(exCast.getMessage());
      } catch (ClassCastException ccEx) {
        throw ex;
    }
  }
  };
}

Handling Checked Exceptions

Let's say you are calling a method which throws some checked exception.

static void foo(int i) throws IOException{}

List < Integer > myList = Arrays.asList(7, 9, 16, 25, 27);
myList.forEach(i - > foo(i));

Since the IOException is a checked exception, we must handle it explicitly, the above line will throw a compilation error-java.lang.Error: Unresolved compilation problem: Unhandled exception type IOException.

Can we put myList.forEach() inside a method and in the method signature we will mention that this method throws IOException? No it will not work, you will still get the same compilation error, because lambda expressions are similar to Anonymous Inner Classes.

By adding a try-catch we can handle Checked Exceptions:

List < Integer > myList = Arrays.asList(7, 9, 16, 25, 27);
myList.forEach(i - > {
  try {
    foo(i);
  } catch (IOException e) {
    throw new RuntimeException(e);
  }
});

This will work fine, but because of try catch our Lambda expression is longer than a small function. 

We can create a FunctionalInterface to handle such scenarios. Let's create a custom functional interface with a single accept method that throws an exception.

@FunctionalInterface
public interface ThrowingConsumer < T, E extends Exception > {
    void accept(T t) throws E;
}

Let's implement a wrapper method that's able to rethrow the exception:

static < T > Consumer < T > throwingConsumerWrapper(
  ThrowingConsumer < T, Exception > throwingConsumer) {
  return i - > {
    try {
      throwingConsumer.accept(i);
    } catch (Exception ex) {
      throw new RuntimeException(ex);
    }
  };
}

List < Integer > myList = Arrays.asList(7, 9, 16, 25, 27);
integers.forEach(throwingConsumerWrapper(i -> foo(i)));

-K Himaanshu Shuklaa..

No comments:

Post a Comment