Java Functional Programming : 6. Function

This is the multi part series on Java Functional Programming

6. Function



Apart from the apply method (which is abstract) the Function functional interface also has a static method and two default methods. We have seen the usage of apply method. It converts (or applies) one type to another. Let us investigate the default and static methods of this functional interface.

Category
Function
Interface Definition
Function<T, R>
Abstract Method
R apply (T t)
Static Method
<T> Function<T,T> identity()
Default Method 1
<V> Function<T,V> andThen(Function<? super R,? extends V> after)
Default Method 2
<V> Function<V,R> compose(Function<? super V,? extends T> before)

Let us see how these methods look from the inside. The static function identity gives back the lambda function which always returns its input argument. The method andThen applies the apply lambda expression and then the result is applied to the expression passed as the input parameter. The method compose is the reverse operation of andThen. In this method first apply is applied to the expression passed as the input parameter and then the result is applied to this expression represented by the functional interface. Phew !! the statements are quite a mouthful.

      
       static <T> Function<T, T> identity() {
              return t -> t;
       }

       default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
              Objects.requireNonNull(after);
              return (T t) -> after.apply(apply(t));
       }
      
       default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
              Objects.requireNonNull(before);
              return (V v) -> apply(before.apply(v));
       }            


Let us first investigate the two default functions. This is how you’d use it in your program.


Function<Integer, Integer> doubleIt = (c) -> 2 * c;
Function<Integer, Integer> squareIt = (c) -> c * c;
             
int doubleAndThenSquareAT = doubleIt.andThen(squareIt).apply(5);  //andThen
int firstSquareThenDoubleAT = squareIt.andThen(doubleIt).apply(5);//andThen

// this does the same thing as above but uses compose instead of andThen
// using compose means we do not have to reorder and the
// compose part is evaluated first
int firstSquareThenDoubleC = doubleIt.compose(squareIt).apply(5); //compose
             
System.out.println("doubleAndThenSquareAT, 5 = " + doubleAndThenSquareAT); //100
System.out.println("firstSquareThenDoubleAT, 5 = " + firstSquareThenDoubleAT); //50
System.out.println("firstSquareThenDoubleC, 5 = " + firstSquareThenDoubleC); //50


One function doubles the integer and the other squares it. We can mix and match the two functions. First we doubled and the squared the integer. In the next line we reordered the process to squaring first and then doubling. In the third calculation we kept the order the same as first but used the compose method instead. The expression within the ‘compose’ is evaluated first.

You would have noticed a static method called identity() in the class Function. What is the use of having a method that returns a reference to itself? Usually you would use them for applying a function to a stream. (We’ll come to the stream later and you can refer this topic back). When you use the method identity() instead of a lambda you avoid creating a new lambda instance every time.  Thus, in the first glance you’d see that there is not much difference between these two lines of code. Both of them would print the output : 123456789



List<Integer>
iList = Arrays.asList(1,2,3,4,5,6,7,8,9);       
iList.stream().map(x -> x).forEach(System.out::print);       
iList.stream().map(Function.identity()).forEach(System.out::print);           
 

But if you run the code below you’d notice that no new instance is created identity() whereas you’ll have a new instance for x -> x.


       Function<Integer, Integer> id1 = Function.identity();
       Function<Integer, Integer> id2 = Function.identity();
             
       Function<Integer, Integer> ld1 = x -> x;
       Function<Integer, Integer> ld2 = x -> x;

       System.out.println(id1);
       System.out.println(id2);
       System.out.println(ld1);
       System.out.println(ld2);          
 


When you run the code, you get the output printed as:

java.util.function.Function$$Lambda$7/250421012@776ec8df
java.util.function.Function$$Lambda$7/250421012@776ec8df
in.java8.funcintf.FunctionExample$$Lambda$9/1283928880@4eec7777
in.java8.funcintf.FunctionExample$$Lambda$10/295530567@3b07d329



<< Prev (Predicate) | Next (TBD) >>

Comments

Popular posts from this blog

Java Generics - 7. Upper and Lower Bounds

Java Functional Programming : 3. The java.util.function Library

Java Generics - 3. Multiple bounding