Java Functional Programming : 2. Lambda Expressions & Java Functional Interfaces

This is the multi part series on Java Functional Programming


2. Lambda Expressions & Java Functional Interfaces



Since this topic could be new to most of the programmers we would address it in an iterative way. We shall throw in the concepts without much detail, and make the programmer understand one aspect of it. Then when that is clear we shall come back and then begin with another aspect. This way in many iterations and repeating different aspects the programmer would get a fair knowledge of the subject. IT is advised that the programmer practice and refers the Java API along the way. No amount of reading can replace the virtues of a first-hand programming.


In programming, a Lambda Function (or Lambda Expression or Anonymous Function) is a function definition which is not bound to an identifier. They are passed as a parameter to higher order function and can be returned back from a function. Java did not have a framework to define just the function and pass them along as a parameter or as a return value. Java does have a concept of anonymous classes that can be used instead. Anonymous classes are defining the class and using it once and then discard it. It can be passed as a parameter too (more on this is taken in a separate topic, earlier). 

Note that java is inherently not a functional programming language and so in its core it does not have a mechanism to pass method references. Java implements this referencing of a method using an indirect (and a little non-intuitive) way. Java has interfaces which contain abstract methods. Java can therefore use these interfaces to reference the lambda expressions that indirectly reference the abstract methods, for which the expressions can be defined later. This however poses a problem, when we have more than one abstract method defined in the interface. Since the lambda expressions can be referenced only by the interface, there is a problem of uncertainty, when more than one abstract method is declared. The java program does not know which abstract method should be called. Java circumvents this problem by making it mandatory that such interfaces which are to be used as lambda expressions must have ‘one and only one’ abstract method. They can have any number of default and static methods (came with Java 8) though. Such interfaces are also known as “functional interface”. Any interface which has one abstract method qualifies for a functional interface. Java also provides for an annotation @FunctionalInterface which can be annotated to the interface to do a compile time check. The annotation is not mandatory for defining a “functional interface” but serves as a marker to perform a compile time check. 



The programmers must define the “function interface” and then define the concrete functionalities when using them. For all practical purposes, a functional interface is just like any other interface, but ideally, they should not be implemented, rather used strictly as an abstraction of a function it defines. If you annotate an interface as a @FunctionalInterface, implementing it as an interface defeats its purpose and is not a good programming construction, even though the java programming language allows it. To understand functional interfaces further, let’s have look at the code. This is a declaration of a functional interface – the @FunctionalInterface annotation is not mandatory but recommended. 


@FunctionalInterface
public interface IntMathOperation {                        
       public int operation(int int1, int int2);                  
}



This functional interface declares an abstract method which takes in two integers and performs some operation over it. The nature of the operation (or the function) is abstract, as it is without a body. Java allows the programmers to write the implementation of these methods without extending them, and these can be passed as references in the method parameters and can be used as return values too. Thus in Java 1.8 a new type of construct called “Lambda” was introduced, with its own unique syntax, which we have never encountered in the earlier version. Declaring a lambda is not that difficult though. What we do is put the number of parameters the method has in parenthesis and the arrow ( -> ) to declare the body.



       IntMathOperation imoAdd = (a, b) -> a + b;
       IntMathOperation imoSub = (a, b) -> a - b;
       IntMathOperation imoMul = (a, b) -> a * b;
             
       System.out.println("Add 4, 5 = " + imoAdd.operation(4, 5));
       System.out.println("Subtract 4, 5 = " + imoSub.operation(4, 5));
       System.out.println("Multiply 4, 5 = " + imoMul.operation(4, 5));


Looking at the code above, we are declaring the method body in the first 3 lines. We have abstracted the function itself and taken the functionality of the method “int operation( int int1, int int2)” outside. The first line declares the addition operation, the second subtraction and the third multiplication. Only one functional interface can be used to declare multiple functions. In this case the same interface is used to perform 3 functions – addition, substraction and multiplication. The values ( a, b ) in the braces correspond to the parameter of the method declaration “int operation( int int1, int int2)”. The ciompiler figures out that a, b are of type int, so no specific declaration is needed. The arrow ( -> ) is the assignment of the abstract function to the interface. The rightmost section is what the action the lambda should be performing. Once the lambda declaration has been done we can reference it with the identifier (imoAdd, imoSub, imoMul in this case), and can be used to invoke the method operation( ). The invoked function would now behave as directed by the lambda expression. The last three lines which invoke the three different types of lambda expression would present the following output:

Add 4, 5 = 9
Subtract 4, 5 = -1
Multiply 4, 5 = 20

I think a question would have crossed your mind by now – Why doesn’t java allow more than one abstract method in its functional interface?  We did touch upon it earlier. It’s simple and obvious. Java is not inherently a functional programming language, so it has to work around with features it currently has, with minimal changes to the language constructs, and supporting backward compatibility. How Java does it is by attaching the lambda expression to the interface (and not to a method reference). If you notice, the reference for the lambda is the interface itself, and not the methods.

IntMathOperation imoAdd = (a, b) -> a + b;

Had there been more than one abstract method in the interface this syntax would not have been possible. Thus, Java disallows more than one abstract method in a functional interface.

Proceeding to the syntax, the above is not the only way to define the lambda expressions. Not all methods would be as simple as a + b. In case the method is complex you can conveniently put the methods in the curly braces and then define the method inside it. All the below are valid lambda expressions and you must choose according to your need. Do not choose the simplest construct. Choose instead what would be more readable to the others reviewing your code.



       IntMathOperation imoAdd1 = (a, b) -> a + b;
       IntMathOperation imoAdd2 = (a, b) -> (a + b);
       IntMathOperation imoAdd3 = (int a, int b) -> a + b;
       IntMathOperation imoAdd4 = (a, b) -> { return a + b; };
       IntMathOperation imoAdd5 = (int a, int b) -> { return a + b; };


Personally, I would prefer the 3rd and 5th declaration because it exactly tells the programmer what the ‘type’ of parameters ‘a’ and ‘b’ is, and not rely on interface’s declaration for clarity. Remember the functional interface would usually be defined in a separate file. For large methods, this would be very informative. It is not necessary to first declare the functional interface and then pass it as a method argument. Though not mandatory it is also advisable to annotate the functional interfaces with the annotation @FunctionalInterface to catch errors in the interface itself and make it self-evident. Java also annotates many of its exiting interfaces such as java.lang.Runnable, java.util.Comparator<T>, etc. which can be used directly as a lambda in the parameter. (Note again, annotating is just marking, it could still be used as a lambda function even if it wouldn’t have been annotated).

<< Prev (Introduction) | Next (The java.util.function Library) >>

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