Blog

Functional Reactive with Core Java - Part 05

By  
Sven Ruppert
·
On May 5, 2018 5:30:00 AM
·

What do we want to achieve?

In the previous section, we have learned how to define a function and how to combine this a little with streams. However, we have always left out the topic of exceptions till now.

Unfortunately, we come across these exceptions again and again in Java. How can one deal with these and which possibilities result from them? We will now have a look at these and try them out now.

In addition to the source text examples given in this article, I will also use the sources from the Open Source project

Functional-Reactive http://www.functional-reactive.org/. The sources are available at https://github.com/functional-reactive/functional-reactive-lib

The exception

In order to see a little how the exceptions behave, we will assume the listed service interface given below in the following discussion. It is a FunctionalInterface, but with the definition of an exception in the method signature.

  public static interface Service {
    String doWork(String txt) throws Exception;
  }

If we now want to use this, then we have a simple implementation in the classical sense. (The implementation itself definitively has no deeper meaning)

    try {
      new Service() {
        @Override
        public String doWork(String txt) throws Exception {
          return txt.toUpperCase() + "-workedOn";
        }
      }.doWork("");
    } catch (Exception e) {
      e.printStackTrace();
    }

Listing: V001

We can very well notice here the try-catch block, which makes us think how we must handle an exception. Should the exception simply be forwarded? Since this is a FunctionalInterface, we can naturally also write it as lambda.

    try {
      ((Service) txt -> txt.toUpperCase() + "-workedOn").doWork("");
    } catch (Exception e) {
      e.printStackTrace();
    }

Listing: V001

If one defines this lambda construct at the level of the class, then initially it looks quite simple.

  public static Service serviceA  = txt -> txt.toUpperCase() + "-workedOnA";
  public static Service serviceB  = txt -> txt.toUpperCase() + "-workedOnB";

Listing: V001

Unfortunately, the try-catch blocks appear again during the usage.

    try {
      final String helloA = serviceA.doWork("Hello A");
    } catch (Exception e) {
      e.printStackTrace();
    }

    try {
      final String helloB = serviceB.doWork("Hello B");
    } catch (Exception e) {
      e.printStackTrace();
    }

Listing: V001

What is now actually with the return value? We still have not thought about it… There are now two ways here. The first one is to write the remaining logic in the try-catch block. If one can do this, then it becomes unmanageable only when further methods are used with exceptions. Does one now start to interconnect the blocks with one another? Or is there a big catch block at the end of all instructions> Everything is quite unattractive.

The second possibility is to try and keep the try-catch block as short as possible. The result value is then stored in a variable, which was defined before the try-catch block. I have decided here to work with an Optional<T>, since this is available in each case in JDK.

    Optional<String> optional;
    try {
      final String result = ((Service) txt -> txt.toUpperCase() 
                                              + "-workedOn").doWork("");
      optional = Optional.of(result);
    } catch (Exception e) {
      e.printStackTrace();
      optional = Optional.empty();
    }

    optional.ifPresent((result)-> System.out.println("result = " + result));

Listing: V002

How would it be, if one would get rid of this try-catch block completely? It is still the same piece of source code.

To understand this point better, we define first of all a function, which can handle the method signatures that define an Exception. In order to make it a FunctionalInterface again, we must think how we envelope the call of the method with the defined Exception.

Similarly, I also assume here that a possible result value will be delivered packed in an optional.

This gives us first of all a function of T on Optional . Since the input type need not be the same as the output type, we define it a little more generally. CheckedFunction<T, R> extends Function<T, Optional<R>>.

We now have a method, which has also defined an exception in the signature. R applyWithException(T t) throws Exception;.

In both the cases i.e., with and without the occurrence of an Exception, the workflow is defined clearly. One can now take this implementation as default implementation for the method signature default Optional<R> apply(T t).

  @FunctionalInterface
  public interface CheckedFunction<T, R> extends Function<T, Optional<R>> {
    @Override
    default Optional<R> apply(T t) {
      try {
        return Optional.ofNullable(applyWithException(t));
      } catch (Exception e) {
        return Optional.empty();
      }
    }

    R applyWithException(T t) throws Exception;

  }

Listing: V003

A classical implementation of this interface then looks like as follows, if we take the previous implementation of the interface service as the basis.

    final CheckedFunction<String, String> checkedFunction
        = new CheckedFunction<String, String>() {
      @Override
      public String applyWithException(String s) throws Exception {
        return ((Service) txt -> txt.toUpperCase() + "-workedOn").doWork(s);
      }
    };

Listing: V003

If now this CheckedFunction is used, one can still see the self-defined method signature. applyWithException. In order to get rid of this again, and since the IDE offers only the usual apply, one can cast it again on a function.

    final Function<String, Optional<String>> f = checkedFunction;

Listing: V003

The usage can now refer to the respective cases, since we usually work with an Optional.

    f.apply("Hello")
     .ifPresent((result) -> System.out.println("result = " + result));

Listing: V003

However, what is missing here is the access to the Exceptionor to the error message, which is returned in case of an error.

To do this, we now modify the CheckedFunction such that we do not use the Optional<T>, but instead the class Result<T> presented in the previous section.

  @FunctionalInterface
  public interface CheckedFunction<T, R> extends Function<T, Result<R>> {
    @Override
    default Result<R> apply(T t) {
      try {
        return Result.success(applyWithException(t));
      } catch (Exception e) {
        final String message = e.getMessage();
        return Result.failure((message != null) 
               ? message 
               : e.getClass().getSimpleName());
      }
    }

    R applyWithException(T t) throws Exception;

  }

In the usage one can now access the extended possibilities provided to us by the result.

    final Consumer<String> print = System.out::println;
    
    final Function<String, Result<String>> checkedFunction
        = (CheckedFunction<String, String>)
          ((Service) txt -> txt.toUpperCase() + "-workedOn")::doWork;

    checkedFunction.apply("Hello")
                   .ifPresentOrElse(
                       (result) -> print.accept("result = " + result),
                       (failed) -> print.accept("failed = " + failed)
                   );

Listing: V004

The CheckFunctions are implemented in the open source project Functional-Reactive https://github.com/functional-reactive/functional-reactive-lib on GitHub and I will access these below. Also present there are CheckedConsumer, CheckedSupplier, CheckedBiFunction,…

We now come to the example, in which we use the CheckedFunction in combination with a stream. One already sees here, how compact the implementations can become during usage. Because even when one is using streams, one still gets confronted with exceptions.

  public static interface Service {
    String doWork(String txt) throws Exception;
  }

  private static IntConsumer print = System.out::println;

  public static void main(String[] args) {
    //from now on using functional-reactive lib
    //https://github.com/functional-reactive/functional-reactive-lib

    final Function<String, Result<Integer>> f 
          = (CheckedFunction<String, Integer>) Integer::valueOf;

    Stream
        .of("1" , "2" , "Hi" , "3")
        .parallel()
        .map(f)
        .filter(Result::isPresent)
        .mapToInt(Result::get)
        .reduce((left , right) -> left + right)
        .ifPresent(print);
        
    //alternative way
    Stream
        .of("1" , "2" , "Hi" , "3")
        .map((CheckedFunction<String, Integer>) Integer::valueOf)
        .flatMap(Result::stream)
        .reduce((left , right) -> left + right)
        .ifPresent(System.out::println);
  }

Listing: V005

The definition of the CheckedFunction has been realized here outside of the stream only for the sake of clarity. One can naturally also write this directly in the mapping.

.map((CheckedFunction<String, Integer>) Integer::valueOf))

Now it can happen that some methods have to be called one after the other and each of which can throw an exception. As an example, one can imagine that several system components must be shut down after a jUnit test.

To ensure now that all methods are called, one must put a try-catch around each method call in the classical case.

  public static interface Service {
    String doWork(String txt) throws Exception;
  }

  public static Service serviceA  = txt -> txt.toUpperCase() + "-workedOnA";
  public static Service serviceB  = txt -> txt.toUpperCase() + "-workedOnB";

    //both must be executed, even if first one fails
    try {
      final String resultA = serviceA.doWork(null);
    } catch (Exception e) {
      e.printStackTrace();
    }
    try {
      final String resultB = serviceB.doWork("Hello");
    } catch (Exception e) {
      e.printStackTrace();
    }

Listing: V006

With the use of CheckedFunction, the source codes essentially become more compact.

    final Function<String, Result<String>> fA 
            = (CheckedFunction<String, String>) (txt) -> serviceA.doWork(txt);
    final Function<String, Result<String>> fB 
            = (CheckedFunction<String, String>) (txt) -> serviceB.doWork(txt);

    final Result<String> resultFA = fA.apply(null);
    final Result<String> resultFB = fB.apply("Hello");

Listing: V006

One can now use this in different variants in a project. Firstly, one can offer the function for implementing the CheckedFunction on a function itself as a function. The function tryIt is named here. One can then specify the transformation by means of lambda or method reference. However, it can also be offered as a static method. tryIt is also named here.

One can see a comparison of both the versions in the listing given below.

  public static interface Service {
    String doWork(String txt) throws Exception;
  }

  public static Service serviceA
      = txt -> txt.toUpperCase();

  public static Function<CheckedFunction<String, String>,
                         Function<String, Result<String>>>
      tryIt = (f) -> f;

  public static Function<String, Result<String>> 
                tryIt(CheckedFunction<String, String> function) {
    Objects.requireNonNull(function);
    return function;
  }

  public static void main(String[] args) {

    Function<String, Result<String>> fA = tryIt.apply(serviceA::doWork);
    Function<String, Result<String>> fB = tryIt.apply(txt -> txt.toUpperCase());

    Function<String, Result<String>> fC = tryIt(serviceA::doWork);
    Function<String, Result<String>> fD = tryIt(txt -> txt.toUpperCase());

    out.println("fA = " + fA.apply(null));
    out.println("fB = " + fB.apply(null));
    out.println("fC = " + fC.apply(null));
    out.println("fD = " + fD.apply(null));
  }

Listing: V007

Now, we do not always have functions of the type string on string. What is still pending now is the generic formulation of what we have learned just now.

Here again we take the path of offering a static method for generating the function.

  public static <T,R> Function<CheckedFunction<T, R>, Function<T, Result<R>>> tryIt(){
    return (f) -> f;    
  }

Listing: V008

I want to mention here again a very nice special feature. Till now, I have implemented the FunctionalInterface and then built a method reference of the instance serviceA. One can naturally extract this from every object instance. The only significant thing here is the special feature that a string was the input value and a string is also the output value. In this way, one can map it to a Function<String, String>. This can also be done with a method, which has two input values and one output value. We then get a BiFunction<X,Y,R>. And so on and so forth. It is worthwhile here to experiment a little.

Thus, one can implement new functionality with the help of functions and continue to use the old implementations. The type safety is there and the IDE will continue providing full support.

Summary

In this part, we have learned how to handle the exceptions. We have gotten rid here of some source code and can, in this way, also use such methods elegantly in combination with streams. It is also seen here that the integration of legacy source code is well possible with the use of method references.

You can find the source code at:

https://github.com/Java-Publications/functional-reactive-with-core-java-005.git

If you have questions or comments, simply contact me at sven@vaadin.com or via Twitter @SvenRuppert.

Happy coding!
Sven Ruppert
Sven Ruppert has been coding Java since 1996 and is working as Developer Advocate at Vaadin. He is regularly speaking at Conferences like JavaOne/Jfokus/Devoxx/JavaZone/JavaLand and many more and contributes to IT periodicals, as well as tech portals.
Other posts by Sven Ruppert