Architecture & DesignJava Lambda Expression Revisited

Java Lambda Expression Revisited

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Lambda expression ushered an evolutionary impetus on how Java code is written in everyday programming. It not only introduced new syntax of expression but also enhanced the capabilities of the API library. The idea induced a way to write code in a manner enhancing clarity and readability. It also enhanced the expressive power of the language to a whole new level. Java lambda expression streamlined much sought functional programming enthusiasm into its core group of its functionality and has lot to offer, either directly into the API library or simply by being a catalyst to new Java features.

The features such as the support for pipeline operation in data, new stream API, for-each style operation, introduction of default behaviour of interface methods have numerous usages, one of them being programming for parallel processing capabilities of multi-core environments. Here, we look into the functional interface in view of Java lambda expression and how it can be used in everyday programming practices in a simple manner.

Functional Interface

Prior to Java 8, methods declared within an interface were purely abstract and had to be explicitly defined in the implemented subclasses. The behaviour is the logic implemented in a set of instructions. There was no concept of default behaviour without explicit function definition in some of its inherited subclasses. Java 8 refurbished the role of interfaces, giving a new meaning called functional interface, which can have one default abstract method. This concept is similar to the anonymous inner class.

A little familiarity with the interfaces, such as java.awt.event.ActionListener, java.util.Comparator, and java.lang.Runnable, will reveal that these are SAM (Simple Abstract Method) interfaces, which have a single abstract method declared in the interface. SAM interfaces are generally used with the help of Anonymous Inner classes, as shown in the following code snippet:

package org.mano.example;

public class TestAnonymous {
   public static void main(String[] args) {
      new Thread(new Runnable() {
         @Override
         public void run() {
            System.out.println("Thread Running ...");
         }
      }).start();
   }
}

This gives a clue to default behaviour implementation through interfaces. A functional interface extended this idea with a syntactic twist where behaviours can be incorporated, even in interfaces. So, how are these functional interfaces represented in Java? They are represented with the help of method reference, constructor reference, or lambda expression. Here, we’ll focus only on the lambda part. And, by the way, there is an optional yet convenient annotation called @FunctionalInterface. An interface designated with this annotation instructs the compiler to check that it must adhere to the functional interface norms; otherwise, flags indicating an error are thrown at compile time.

The simplest way to define a functional interface is this:

package org.mano.example;

interface FunctionalInterface {
   int getNumber();
}

Observe that a single method is declared in the interface, which is implicitly abstract. In other words, this single method is the tacit definition of its default behaviour. Now, let’s specify the lambda expression in the context of its target type to initialize a number, as follows:

package org.mano.example;

public class TestFrame {

   public static void main(String[] args) {
      FunctionalInterface f;
      f=()->100;
      System.out.println("Number is "+f.getNumber());

   }
}

Output: Number is 100

Lambda expression, in association with target type context, ends up transforming a code segment into an object similar to creating a class instance of the functional interface. The lambda expression defined in the main function (refer to the preceding code) is the defined behaviour of the abstract method declared in the interface. The getNumber() method simply returns the value assigned by the lambda expression. Note that lambda expression is for a target type of the abstract method and the abstract method itself must be of the same type. That means in the above code we cannot write something like:

f=()->100.2345; or f=()->"100.2345"   // not allowed

This incompatibility is due to the declared int type and assigned double or String literal. The assigned type must be compatible with method type.

Generic Functional Interface

Lambda expressions cannot be generic because they inherently cannot specify type parameters. However, a functional interface can be generic and lambda expression in association with the functional interface can exhibit generic qualities. In such a case, the lambda expression type is determined by the type argument of the declared functional interface. For example:

package org.mano.example;

public interface GenericFunctionalInterface<T> {
   public T genericFunction(T t);
}


package org.mano.example;

public class TestFrame {

   public static void main(String[] args) {
      GenericFunctionalInterface<String> palindrome=(str)->{
         boolean flag=true;
         for(int i=0;i<str.length()/2;i++){
            if(str.charAt(i)!=str.charAt(str.length()-(i+1))){
               flag=false;
               break;
            }
         }
         return String.valueOf(flag);
      };
      System.out.println(palindrome.genericFunction("madam"));

      GenericFunctionalInterface<Double> toRadian=(degree)->{
         return (degree*Math.PI)/180;
      };

      System.out.println(toRadian.genericFunction(90.0));

   }
}

Observe how we have defined the abstract generic method declared in the functional interface in totally different manner. This is the power of lambda expression in association with a functional interface.

Predefined Functional Interfaces

There are a number of predefined functional interfaces introduced by Java 8 in the java.util.function package, such as Function<T,R>, where we can operate on an object of type T and return an object of type R, UnaryOperator<T>, BinaryOperator<T>, and so forth. Refer to the Java documentation for complete details. These interfaces can be leveraged in programming without the need to write a functional interface from scratch. For example, Function<T,R> can be used as follows:

package org.mano.example;

import java.util.function.Function;

public class TestFrame {
   public static void main(String[] args) {
      Function<Double, Double> toDegree = (radian) -> {
         return (radian*180)/Math.PI;
      };
      System.out.println(toDegree.apply(1.5707963267948966));
   }
}

Conclusion

A functional interface thus is an interface with a single abstract method that can be used effectively with lambda expression in number of situations. Lambda expression enhances code clarity and readability. A generic abstract method declared in a functional interface can have polymorphic usage, open to different definitions. Predefined functional interfaces of the Java API library are optional yet quite convenient enough to leverage a reusability paradigm rather than re-inventing the wheel every time one needs a functional interface.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories