Java 8: Introduction to Lambda Expressions and Functional Interfaces

As Java evolved, many new features have been added to make the language more concise and streamlined. One such addition is the Lambda Expressions which adds new syntax elements to the language and incorporates new capabilities such as taking advantage of parallel processing, etc.

Before moving on to Lambda Expressions, we should understand some key concepts that are the building blocks for Lambda Expressions.

Functional Programming

Functional programming refers to the programming paradigm which is based on mathematics, i.e., it treats computation as mathematical calculations. Here programming is in the form of expressions and declarations rather than statements.

Another important fact about functional programming is that it does not allow changing of state and mutable data. Once the arguments are passed, it produces a certain result based on the parameters which do not change even if the same arguments are passed again.

Almost all of the Functional Programming is based on lambda calculus in Mathematical logic and is used for expressing computation based on function abstraction and application using variable binding and substitution. It is a Turing complete language and can simulate any Turing machine.

Lamba Expressions were added to Java to bring about the features of Functional Programming in Java. Although primarily an Object Oriented Programming language, adding Lambda Expressions is just a way of increasing the simplicity of the code. Lambda Expressions have already become a huge part of computer language design with programming languages like C#, C++, Haskell, Python, etc. already implementing it.

Functional Interfaces

A Functional Interface is the one that has only one abstract method which represents a single action. It is also referred to as a SAM(Single Abstract Method) type.

For Example, the Runnable interface is a functional interface as it only defines one abstract method that is the run() method.

A functional interface can specify any public method defined by the Object class without affecting its status as a functional interface as the public methods of the Object class are implicitly implemented by a functional interface.

Lambda Expressions can be used in the context where a target type is specified. And this target type is defined by a functional interface. The following code snippet is an example of a functional interface :

interface MyInterface {
    String myName();
}

Note: Prior to  JDK 8, all the methods inside an interface were considered to be abstract methods. But after JDK 8, with the introduction of default methods and static methods, it is possible to specify the default implementation for a method inside an interface.

Introduction to Lambda Expressions

A Lambda Expression can be defined as an unnamed or anonymous method. They are also known as closures and are used to provide an implementation for the abstract method defined by the functional interface.

Lambda method cannot be executed on its own. This method is very similar to an anonymous class. But one drawback of an anonymous class in case of a functional interface is that in cases of very simple implementation like a functional interface it becomes unwieldy and unclear. Thus, it is better to use a lambda expression here.

Lambda Operator

As we already read that Lambda Expressions add new syntax elements into the Java language. There’s another new addition which comes with it, which is the lambda operator or the arrow operator (->). This divides the expression into two parts. The first or the left side consists of the parameters(very much like methods).

These parameters do not need to have their type specified explicitly as it can be inferred automatically in most cases. A lambda expression can have any number of parameter. If there are no parameters then the programmer can just put empty brackets in its place. Eg:

(parameter1, parameter2, ...) -> { /** body */ }
() -> 5 //no parameters (n) -> n+10 //single parameter OR
n -> n+10 // Missing bvrackets but this is p[ossible only with 1 parameter

The second side or the right side specifies the body of the lambda expression. It tells us the action of the lambda expression which can either be a single expression or a  block of code.

Functional Interface and Lambda Expressions

As we already saw that Functional Interfaces are the ones that contain only one abstract method in their body. Prior to JDK 8, all methods inside an interface were implicitly abstract but after the introduction of JDK 8, we can have a default implementation for the methods inside the interface body.

When it comes to lambda expressions, we know that they cannot be executed on their own. Thus they usually specify the implementation of the abstract method defined by the functional interface that defines its target type. So, lambda expressions are only specified in the context where we have the target type defined. These contexts are created when a lambda expression is given to a functional interface reference. Examples of
target type context are return statements, method arguments, variable initialization, etc.

Note: To use the lambda expression in a specific target type context, the parameter type of the abstract method and the lambda expression must be compatible.

Program to show the use of Lambda Expressions

/**
 * This program shows how to use the lambda expression. 
 * It defines a functional interface called InterfaceDemo with the function getValue().
 * This function returns an integer value.
 */
package com.codingeek.Java8.lambdaExpressions;

//The Functional Interface
public interface InterfaceDemo {
  int getValue();
}

public class LambdaDemo {
  public static void main(String args[]) {
    
    InterfaceDemo interDemo; //instance of the functional interface
    
    //In this example we r simply returning a fixed value for the getValue function. 
    //Note that the getValue method does not have any parameters.
    interDemo = () -> 456;
    System.out.println("A constant integer: " + interDemo.getValue());
    
    //Note that the target type should be compatible to the one defined in the functional interface.
    //The example given below will produce an error because of the incompatible data types.
    //The return type of the function is int where here we are returning a String.
    //interDemo = () -> "Codingeek";
    
    //In this case the function would not accept the double value which is returned by the Math.random() function.
    //So we explicitly covert the value to int.
    interDemo = () -> (int) Math.floor(Math.random() * 100);
    System.out.println("A Random Integer: " + interDemo.getValue());
  }
}
Output:-
A constant integer: 456
A Random Integer: 83

An investment in knowledge always pays the best interest. Hope you like the tutorial. Do come back for more because learning paves way for a better understanding.
Do not forget to share and Subscribe.
Happy coding!! 🙂

Recommended -

Tags:,