Thursday 16 March 2017

Functional Programming in java

Java 8 introduced a new feature called lambda expressions. Before Java 8 it only followed object oriented programming and its concepts. Now it has allowed to write functional programming as well by passing code as an argument to a method. Functional programming is a hot topic for some other programming languages.  But Java doesn't had any feature like that before java 8.
In this blog I will try to cover following topics:
  1. Lambda expression
  2. Functional interface
  3. Variable capture
  4. Method References
  5. Default method
Before starting anything, lets come to questions of WHY and then HOW.


Why lambdas are required :
  1. Java does not have a feature of closure earlier.
  2. Lambda expressions have some advantages over inner anonymous classes.Inner anonymous classes were difficult to write and understand.
  3. Make your programming multi core. Programmer just need to provide the task to programming language and language should itself judge how to make it multi core by calling some specified methods.

Above were the reasons for providing lambda expressions.Now lets understand how it can be used.
Below can be a informal definition for lambda :
lambda expression = method of a class - method signature

Below are the four different ways to iterate over a list of integer.
       
List<Integer> numbers = Arrays.asList(10,20,30,40,50);

for(Integer x : numbers) {
 System.out.println(x)
}

numbers.stream().forEach(x -> System.out.println(x));

numbers.stream().forEach(x -> { System.out.println(x);} );

numbers.stream().forEach((Integer x) -> { System.out.println(x); } );
As you can see, we are passing some expression to forEach() method.
Content before "->" operator are arguments to lambda expression and content to its right is the actual code.

There is term named Functional interface, which is important to understand while understanding lambda expression in java.

Functional interfaces

Any interface that has exactly one and only one method. You just need to pass a lambda expression for this kind of interface implementation, like anonymous classes. @FunctionalInterface annotation has been provided for these kind of interfaces. Interfaces which have number of methods not equal to one cannot be marked as @FunctionalInterface. lambda expressions can only be passed to a FunctionalInterface. Any interface, which has exactly one method, will be considered as a FunctionalInterface even if it not marked with annotation @FunctionalInterface. By this it will also support backward compatibility to interfaces of previous versions of java. Interface with one method and one or more default methods is also FunctionalInterface.

Example of functional interface will be Runnable , Callable.
       
Callable<String> callableObject = () -> "task completed";
Runnable runnableObject = () -> System.out.println("done");

Please note that, if method of functional interface throws any checked exception, then there is no need to provide any throws clause for lambda expression.Basically, lambda expression can be considered as an object of implementation of an interface, like anonymous class object.The method generated from lambda expression must have same signature as the method in the functional interface.

Now lets compare lambda expressions with anonymous class in java.

Variable Capture

How variable outside the scope of lambda expression and anonymous class are used.
Code inside lambda expression uses :
  • method local variable without final     //valid
  • changing local variable without final  //not valid
  • fetching class static variables          //valid
  • fetching class non-static variables    //valid
       
class TestVariableCapture {
    int x = 20;
    static int y = 30;

    void variableCapture() {
        int x = 10;
        int z = 40;
  
        Runnable runnableObject = () -> {
            System.out.println(z);   //40
            System.out.println(z++); //compile time error
                                     //Local variable z defined in an enclosing scope must be final or effectively final
            System.out.println(x);   //10
            System.out.println(y);   //30
            System.out.println(this.x); //20
        };
        runnableObject.run();
    }
 
    public static void main(String args[]) {
        TestVariableCapture ob = new TestVariableCapture();
        ob.variableCapture();
    }
}

  
Anonymous class can have static or non-static class level variables, but lambda expression cannot. Using this keyword inside a lambda expression will be same as current object of calling method unlike anonymous class which behaves differently. If you want to use this keyword inside anonymous class, you need to use "ClassName.this" .

Difference between lambda expression and anonymous classes:
  1. Anonymous classes can have a state, via class level variables
  2. Anonymous classes can have more than one methods, lambda expression has only one non default method
  3. this keyword points to an object instance for an anonymous inner class but points to the enclosing object for a lambda expression

java.util.function package has 43 commonly used functional interfaces.
  • Consumer<T> : function that takes a single argument of type T and returns void
  • Supplier<T> : function that takes no argument and returns a result of type T
  • Predicate<T> : function that takes a single argument of type T and returns boolean
  • Function<T,R> : function that takes a single argument of type T and returns result of type R
  • BiFunction<T,U,R>: function that takes two input arguments T, U and returns type R
  • BiConsumer<T,U> : function that takes two input arguments T, U and returns void
and many more.
Just in case if your requirement is fulfilled by above interfaces,you can use these and no need to create your own.

Example 1:
       
Consumer<Integer> printAges = x -> System.out.println(x);
List<Integer> ages = Arrays.asList(11,22,3,34,51);
ages.stream().forEach(printAges);

Example 2:
       
Predicate<Integer> isAdult = age -> {
    if (age <= 18)
        return false;
    else
        return true;
};
List<Integer> ages = Arrays.asList(11,22,3,34,51);
ages = ages.stream().filter(isAdult).collect(Collectors.toList());

Target Typing

As we know java is typed language.Every variable must have some data type and it should be understood by compiler. So how lambda expression is understood by java source compiler. There is a term used with lambda expression as Target Typing. In other languages, function is represented as data type.java could have used function as a data type but it would break its backward compatibility. Lambda expression has a type of Functional interface.Lambda expression also supports Target Typing. Type of lambda expression is inferred from the surrounding context. Left hand side of expression is inferred from right hand side expression.
For example :
       
Callable<String> obj1 = () -> "complete";
PrivilegedAction<String> obj2 = () -> "complete";
Both lambda expression looks same, but they are assigned to different interface reference.

Method References

lambda is a way to define an anonymous function. But what if that function is already written. Method references can be used to pass an existing function in place where lambda expression is expected. 
:: operator is used to perform this feature.
       
class Example {
    public void printInt(Integer x) {
        System.out.println(x);
    }
 
    public static void printDouble(Double x) {
        System.out.println(x);
    }
 
    public static void main(String args[]) {
        Example ob = new Example();
        Consumer<Integer> intConsumer = ob::printInt;
        intConsumer.accept(10);
  
        Consumer<Double> doubleConsumer = Example::printDouble;
        doubleConsumer.accept(20d);
    }
}

References to a Constructor
       
Function<String,Integer> stringToInt1 = x -> new Integer(x);
System.out.println(stringToInt1.apply("100"));

Function<String,Integer> stringToInt2 = Integer::new;
System.out.println(stringToInt2.apply("200"));

Default Methods

Java wanted to add lambda in collection framework, but they do not wanted to change Collection API interfaces. This is known as Interface evolution problem. They wanted to integrate stream API changes with Collection API but they also wanted to make java backward compatible with older versions.
Default methods enable you to add new functionality to the interfaces of your libraries and ensure binary compatibility with code written for older versions of those interfaces.

Methods like stream(), parallelStream() are added to Collection interface as default.
Similarly forEach() and spliterator() are added to Iterable interface as default.

Go and read source code for these methods and you will realize importance of default in Java. So many changes done in API and no client has to change their code while moving forward or backward in versions.We can revisit the definition of functional interface.

Functional Interface is an interface that has exactly one non-default method.

lets figure out some examples of default methods in different scenarios.

Default method inheritence
       
interface Test {
    default void f1() {
        System.out.println("Test");
    }
}

class TestImpl implements Test {
    public static void main(String args[]) {
        TestImpl ob = new TestImpl();
        ob.f1();  //Test
    }
}

Overriding default method
       
interface Test {
    default void f1() {
        System.out.println("Test");
    }
}

class TestImpl implements Test {
    @Override
    public void f1() {
        System.out.println("TestImpl");
    }

    public static void main(String args[]) {
        TestImpl ob = new TestImpl();
        ob.f1();  //TestImpl
    }
}

Hierarchy of default methods
       
interface A {
    default void f1() {
        System.out.println("A");
    }
}

interface B extends A {
    default void f1() {
        System.out.println("B");
    }
}

class C implements B {
    public static void main(String args[]) {
        C ob = new C();
        ob.f1();  //B
    }
}

Default method conflict
       
interface A {
    default void f1() {
        System.out.println("A");
    }
}

interface B {
    default void f1() {
        System.out.println("B");
    }
}

interface C extends A,B { // compile time error
    // Duplicate default methods named f1 with the parameters () and () are inherited from the types B and A
}

class D implements A,B { // compile time error
    // Duplicate default methods named f1 with the parameters () and () are inherited from the types B and A
}

Resolving default method conflict
       
interface A {
    default void f1() {
        System.out.println("A");
    }
}

interface B {
    default void f1() {
        System.out.println("B");
    }
}

interface C extends A,B {
    default void f1() {
        A.super.f1();
        System.out.println("C");
    }
}

class D implements A,B {
    void f1() {
        B.super.f1();
        System.out.println("D");
    }
}

Diamonds are no problem

default method of nearest level from bottom of the tree will be called. If more than one default methods are found at same level of tree, then its compile time error, and you need to override that method for you.
       
interface A {
    default void f1() {
        System.out.println("A");
    }
}

interface B extends A {
    default void f1() {
        System.out.println("B");
    }
}

interface C extends A {
    default void f2() {
        System.out.println("C");
    }
}

class D implements B,C {
    public static void main(String args[]) {
        D ob = new D();
        ob.f1();  //B
    }
}

Hoping that all these features of Java 8 will be clearer to you after reading this blog.Content of this blog has been collected from various sources mentioned below.
Java 8 lambda expressions and streams
Java 8 target typing
State of the Lambda
Lambda: A Peek Under the Hood
Java 8 Streams: Lambda in Top Gear

Thanks

3 comments:

  1. Explaining these core concepts, it looks very simple

    ReplyDelete
  2. Now I know importance of functional programming in java, Well explained

    ReplyDelete