Java and J2EE Tutorials, Jsp and Servlet Tutorials, Spring MVC, Solr, XML, JSON Examples, Hibernate & Struts 2 Hello World projects



Saturday, 27 February 2016

Java 8 Lambda Expressions (Functional Interfaces, Variable Capture, Method References, Default Methods)

In this article we will discuss about most exiting release of Java ever, Java 8 and its new features like Lambda expressions and Streams.

Java 8 Lambda Expressions

In this particular section we will come to know syntax and functioning of Java 8 Lambdas, following are the breakups:

1) Concept
2) Syntax
3) Functional Interfaces
4) Variable Capture
5) Method References
6) Default Methods


Java 8 Lambda Concept

The Mathematica foundation of Lambda Calculus was stated in 1930 and than most of the programming languages started using the concept from 1960 to till data, before Java other languages like C++, Python, JS etc are already having the concept in them.

What are Lambdas good for ? : Lambda expressions are the basics of functional programming; can be used to define anonymous functions, can be assigned to a variable, can be passed to a function and can be returned from a function. Other than this lambda expressions made the parallel programming easier, help programmers to write compact, cleaner and richer data structures code and API's.


Java 8 Lambda Syntax

We have seen what are lambda theoretically, what are other languages already using the concept and how can lambda help us in cleaner code writing; now lets see how Java 8 does it:

Example 1: Printing a list of integers
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.forEach(i -> System.out.print(i));

Output: 12345

forEach() is a method that accepts a function as input and calls the function for each value of the collection. i -> System.out.print(i), is an lambda expression, this defines an anonymous function with one parameter i of type Integer.

Example 2: Printing a list of integers by adding 10 to them
 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

  list.forEach(i -> {
   int addNumber = 10;
   i = i + addNumber;
   System.out.print(i);
  });

Output: 11,12,13,14,15,

As said earlier we can pass a function to forEach(), if we have single line we don't need to put body of the function {} as in Example 1, but for multiple line we need ro put {} as in Example 2. we can define variable, do calculation and everything that can be done in a body of a function.

Example 3: Function parameter with a type
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

list.forEach((Integer i) -> System.out.print(i + ","));

Output: 1,2,3,4,5, We can define the type of parameter “i” as Integer, but that is optional because Java compiler is smart enough to identify the type. Lambda Expression Life cycle Lambda expressions do have a life cycle as well, when compiler sees a lambda expression than it converts it to a function and than calls that function:
i -> System.out.print(i) // lambda expresson 

is converted to:
 public static void generatedNameForLambda(Integer i){
  System.out.println(i);
 }



Java 8 Lambda Functional Interfaces

Any Java developer around the globe has used at least one of the followig interface in their application: java.lang.Runnable, java.awt.event.ActionListener, java.util.Comparator, java.util.concurrent.Callable. These interfaces has one thing in common, that these interfaces had only one method in them. These interfaces can be represented uisng Java 8 Lambda expression. There is a @FunctionalInterface annotation to put on functional interfaces, this will throw an error if put on a wrong implementation of functional interface.
 @FunctionalInterface
 public interface SimpleFuncInterface {
   public void doWork();
 }

So this @FunctionalInterface annotation has a backword compatibility as well, any interface having one method even compiled with Java 1.0 is comatible with Java 8 as a functional interface.

Example 4: Assigning lambda to interfaces

Now we can assign lambda expressions to these functional interfaces as well, we have an functional interface named Consumer in java.util.function:
 public interface Consumer<T>{
  void accept(T t);
 }

We can assign a lambda expression to this Consumer<T> iterface as follows :
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Consumer<Integer> consumer = i -> System.out.print(i);
list.forEach(consumer);

Output: 12345

Exampel 5: Return an lambda expression
 @FunctionalInterface
 interface Calculator {
  public Integer add(Integer i1, Integer i2);
 }

 public Calculator addNumbers() {
  return (i1, i2) -> i1 + i2;
 }

 public void doSomething(Calculator c) {
  System.out.println(c.add(123, 120));
 }

Calculator c = addNumbers();
doSomething(c);

Output: 243

Here we can see addNumbers() is returning an lambda expression, we have already seen lambda expressions can be assigned or represented as functional interfaces.


Java 8 Lambda Variable Capture

Variable capture is nothing but the ability of lambdas to identify and use variables outside the expression. Local variable

Example 6: Capturing a local variable inside expressions
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int v = 10;
list.forEach(i -> System.out.print((i + v)+","));

Output: 11,12,13,14,15,

This is simple, local variables outside expression can be used easily. But if we try to modify local variables defined outside expression, the compiler will through an exception saying: “local variable v defined in an enclosing scope must be final or effectively final”
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
  int v = 10; 
  list.forEach(i -> {
   v++;  // will not compile
   System.out.print((i + v) + ",");
});


Example 7: Capturing a static variable inside expressions

The same is true with static variables as well:
 static int v = 10;
 public static void main(String[] args) {

  List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
  
  list.forEach(i -> {
   System.out.print((i + v) + ",");
  });
 }

Unlike anonymous inner classes, this keyword in lambda expressions points to the this of enclosing class.


Lambda are not anonymous inner classes

Lambda expressions looks like anonymous inner classes but they are completely different, below are the points in favor of that:

1) Anonymous inner classes can have states in form of class level instance, but lambda can not have that.

2) Anonymous inner classes can have a number of different methods in them, but lambda can have single method only.

3) Keyword “this“ ponts to the object instance of the anonymous inner class but “this” in labda points to the enclosong class.



Java 8 Lambda Method References

So far we have seen, we can pass a anonymous function as a lambda during lambda expressions, but we can also reference an already created function in a lambda expression as follows:

Example 8: Passing a reference of existing method as lambda
public class Impl {
 public static void main(String[] args) {

  // way 1 of passing existing static method as lambda
  Consumer<Integer> consumer1 = i -> doSomething(i);
  consumer1.accept(1);

  // way 2 of passing existing static method as lambda
  Consumer<Integer> consumer2 = Impl::doSomething;
  consumer2.accept(1);
 }

 public static void doSomething(Integer i) {
  System.out.println(i);
 }

}

Output: 1 1

We have an elready created static method doSomething(Integer I), we can pass it to functional interface Consuler<Integer> because both accept(Integer I) and doSomething(Integer I) have same signature. We can do the same in two ways as shown above. In the examples above the code works because Consumer<T> is having accept(Integer I) single paramater of type integer and does not return anything and same is the condition with doSomething(Integer I).

To pass a already exiting method as a function in lambda expressions; the signature of referenced method need to match with signature of functional interface method.

Example 9: Referencing a constructor
A constructor can be passed to a mapper in following two ways:
    // way 1 of passing existing constructor as lambda
  Function<String, Integer> mapper1 = x -> new Integer(x);
  System.out.println(mapper1.apply("12"));

  // way 2 of passing existing constructor as lambda
  Function<String, Integer> mapper2 = Integer::new;
  System.out.println(mapper2.apply("11"));

Output: 1

In the above code we are telling the compiler to create a method and in the body of method accept String parameter and convert it to Integer.


Java 8 default methods in Interfaces

When Java 8 features start building the top most challenge was back support meaning that, every new functionality should not affect existing code.
default void forEach(Consumer action)

For instance we have a java 8 method forEach() in Iterable<T> interface, as per old interface conventions if we have unimplemented methods in an interface than its implementation class has to override it and hence all List implementation were forced to override forEach(). The alternative to that was created with default methods, these methods has not to be implemented by any implementation class forcefully.

Example 10: Default methods
interface InterfaceA{
 public void a();
 default void b(){
  // some implementation
 }
}

class ClassA implements InterfaceA{
 @Override
 public void a() {
  // TODO Auto-generated method stub
  
 }
}

The code written above works fine and does not give any compilation error, we have a non-abstract method in InterfaceA that workds because a new keyword “default” can be used for doing so. Secondly we are not forced to override b() in implementation class InterfaceA. This is where we needed to have default type of methods.

The default methods written in interfaces are already having an impersonation and do not forced the implemented class to inherit them, but one can inherit them as well as in traditional manner and in that case the overridden functionality will be taken into account:
interface InterfaceA {
 public void a();

 default void b() {
  System.out.println("InterfaceA");
 }
}

class ClassA implements InterfaceA {
 @Override
 public void a() {
  // TODO Auto-generated method stub

 }

 @Override
 public void b() {
  System.out.println("ClassA");
 }
}


This is all for this article, we have seen a lot of implmentation od lambda expressions. These expressions keeps the code compact and are very handy while dealing with Java 8 Streams. We will see more bout Java 8 Stream in next examples.


0 comments:

Post a Comment

Like Us on Facebook


Like Us On Google+



Contact

Email: neel4soft@gmail.com
Skype: neel4soft