Java. Lambda expressions. Basic concepts. Functional interface. Generalized functional interfaces and lambda expressions. Examples




Java. Lambda expressions. Basic concepts. Functional interface. Generalized functional interfaces and lambda expressions. Examples


Contents


Search other Web sites:

1. The concept of lambda expression. The advantages of using lambda expressions

Lambda expressions were introduced in JDK 8 to enhance the Java language. Lambda expressions are also called closures.

The advantages of using lambda expressions in the Java language include the following:

  • the use of new elements that increase the expressiveness of the Java language. The implementation of some common constructions is simplified;
  • expanding the capabilities of the program interface library (API). This can include simplifying parallel processing, improving the work with I/O streams in the API.

The advent of lambda expressions has fueled new features in the Java language such as default methods and the use of method references without executing them.

 

2. Lambda expression (closure). General form. Lambda operator ->. Examples

The implementation of any lambda expression is based on the use of two language constructs:

  • directly lambda expressions;
  • functional interface.

A lambda expression is an anonymous (unnamed) method that does not execute on its own, but implements a method declared in a functional interface (see point 3). If a lambda expression is encountered in the program, this leads to the creation of some anonymous class containing an anonymous method, the code of which is defined in the lambda expression.

When declaring a lambda expression, the lambda operator is used, which is denoted by the symbols -> (arrow). The lambda operator is interpreted as “becomes” or “passes”. The lambda operator (->) splits a lambda expression into two parts: left and right. On the left side of the lambda expression, parameters are specified. On the right side, there are actions (operators) that define the code of the lambda expression.

The lambda expression code can be generated in one of two ways:

  • contain a single expression. In this case, the lambda expression is said to be single;
  • enclosed in curly braces { }. This is the case when several operators need to be specified on the right side of the lambda operator. In this case, the lambda expression is called a block expression. When returning a result in block lambda expressions, you must specify the return statement. In the simplest case, a block lambda expression declaration can be as follows:
(list_of_parameters) -> {
  // Lambda expression body
  // ...
  return result;
}

here

  • list_of_parameters – a list of parameters that the lambda expression receives. The list of parameters is specified, separated by commas, just like in the method. The parameter list is indicated on the left side of the lambda operator. The parameters of the lambda expression must be compatible in type and number with the parameters of the abstract method that is declared in the functional interface.

If the lambda expression contains a single parameter, it can be without parentheses ( ):

i -> {
  // ...
}

It is possible that the lambda expression does not receive parameters. In this case, the general form of a block lambda expression is

() -> {
  // Lambda expression body
  // ...
}

If the lambda expression uses one operator (expression), then the curly braces can be omitted:

(list) -> expression;

here

  • list – the list of parameters
  • expression – expression to be calculated using the lambda expression.

Example 1. Below is a lambda expression without parameters that returns the number π:

() -> Math.PI

In the above code, the result of the lambda expression is the PI constant from the Math class. Another solution could have been written

() -> 3.1415

Example 2. A lambda expression that takes two integer parameters and returns their multiplication.

(int a, int b) -> a*b

Example 3. A lambda expression that, given the lengths of the three sides a, b, c returns the area of a triangle.

(double a, double b, double c ) -> {
  if (((a+b)<c) || ((a+c)<b) || ((b+c)<a))
    return 0.0;
  else
  {
    double p = (a+b+c)/2; // semiperimeter
    double s = Math.sqrt(p*(p-a)*(p-b)*(p-c)); // Heron's formula
    return s;
  }
}

 

3. Functional interface. Definition. General form. Examples

Functional interface is an interface that contains only one abstract method. The functional interface defines only one action (operation). The functional interface defines the target type of the lambda expression. The functional interface is also called SAM-type (Single Abstract Method).

In the most general case, a functional interface declaration looks like this:

interface InterfaceName {
  return_type MethodName(list_of_parameters);
}

here

  • InterfaceName – the name of the functional interface;
  • MethodName – the name of the method that defines the purpose of the interface;
  • return_type – the type that MethodName returns;
  • list_of_parameters – a list of parameters for the MethodName method.

If two or more abstract methods are declared in an interface, then this interface is not considered a functional interface.

Example 1. A functional interface is declared that defines an abstract method that takes no parameters and returns an integer

// An interface that defines a parameterless method that returns an integer
interface INumber {
  int GetNumber();
}

The lambda expression code that implements this interface can be, for example, the following:

// Lambda expression that implements the INumber interface
INumber lambdaNumber = () -> 255;

After the lambda expression is formed, you can call the GetNumber() method of the INumber interface

// Using a lambda expression as the GetNumber() Method
int number = lambdaNumber.GetNumber(); // number = 255
System.out.println("number = " + number);

Example 2. A functional interface is declared that defines a method, receives 3 numbers of type double and returns a value of type double

// An interface that defines an abstract method
// that receives 3 double parameters and returns a double.
interface ICalculation {
  double CalcMethod(double a, double b, double c);
}

For such a method signature, you can implement lambda expressions that perform various operations on three numbers, for example:

  • calculate the sum of three numbers;
  • calculate the maximum (minimum) value among the three numbers, etc.

Following is the construction and use of a lambda expression that calculates the sum of three numbers

// Forming the CalcMethod() method, which calculates
// the sum of 3 numbers of double type using a lambda expression
ICalculation ICalc = (a, b, c) -> a+b+c;

// 2. Calculate the sum of integers 1 + 2 + 3
int sum = (int)ICalc.CalcMethod(1, 2, 3);
System.out.println("sum = " + sum);

As you can see from the above code, you can use integer types in a method that operates on the double type.

 

4. Sequence of steps for building lambda expressions

In order to use lambda expressions in the program, you need to perform the following steps:

  1. Declare a functional interface I containing only one abstract method M. The signature of the parameters of the M method and the type of the returned result must correspond to the problem being solved.
  2. Declare a Ref reference to functional interface I in the method where you want to use the lambda expression.
  3. Generate the lambda expression code according to the syntax and assign this code to the Ref to the functional interface. The number of parameters in the lambda expression must match the number of parameters that are declared in method M of functional interface I.
  4. Using the Ref reference, call the method M. The call looks like this:
Ref.M(parameters)

here parameters – list of parameters of the M() method.

 

5. Solving problems with lambda expressions
5.1. Lambda expression that processes three numbers

Create and invoke a lambda expression that evaluates:

  • the sum of three numbers;
  • the maximum value of three numbers.

Implement the task using a functional interface.

package TrainLambda;

// Declare a functional interface
interface MyNumber {
  // A method that takes three double parameters
  // and returns the result of double type
  double GetValue(double a, double b, double c);
}

public class TrainLambda01 {

  public static void main(String[] args) {
    // Using the functional interface MyNumber
    // 1. Declare a reference to functional interface
    MyNumber mn;

    // 2. Assign a lambda expression to the mn reference,
    //    here an instance of the class with the implementation
    //    of the GetValue () method is created. The mn reference
    //   is a reference to this class instance.
    mn=(a,b,c)->a+b+c; // single lambda expression

    // 3. Call the GetValue() method and display the result
    double res = mn.GetValue(5, 7.5, 3.8);
    System.out.println("res = " + res);

    // --------------------------------
    // 4. Calculating the maximum of three numbers
    //    Generate a new block lambda expression
    mn=(a,b,c) -> {
      double max=a;
      if (max<b) max=b;
      if (max<c) max=c;
      return max;
    };

    // Calling the GetValue() method and displaying the result
    double max = mn.GetValue(5.3, 8.3, 4.2);
    System.out.println("max = " + max);
  }
}

The result of the program

res = 16.3

 

5.2. Lambda expression that is used in a method parameter to process an array of numbers

Implement and demonstrate a method that calculates the sum of paired and unpaired elements of an integer array. The method must receive as a parameter a lambda expression that determines the parity of the element and use it for calculations. Demonstrate the method for lambda expressions that:

  • determine if the array element is paired;
  • determine if the array element is unpaired. 
package TrainLambda;

// A functional interface that checks a number for parity.
// This interface contains only 1 method
// that takes an integer and returns a boolean result.
interface Odd {
  // An abstract method for checking the parity of an int number
  boolean IsOdd(int d);
}

// A class containing the method that calculates the sum of the elements of an array
class SumArray {
  // A method that calculates the sum of the elements of array.
  // The sum is calculated using the return result from the lambda expression.
  // The method receives 2 parameters:
  // - A is the array to sum;
  // - refLambda - reference to Odd functional interface.
  public int Sum(int[] A, Odd refLambda) {
    int sum=0;
    for (int i=0; i<A.length; i++)
      if (refLambda.IsOdd(A[i])) // is the array element paired?
        sum+=A[i];
    return sum;
  }
}

public class TrainLambda01 {

  public static void main(String[] args) {
    // Passing a lambda expression to a method as an argument.
    // 1. Declare a reference to the interface Odd
    Odd ref1;

    // 2. Assign lambda expression to ref1
    ref1 = (d) -> {
      // parity check code
      if (d%2==0) return true;
      else return false;
    };

    // 3. Demonstrate calling the Sum() method of the SumArray class and passing it a lambda expression
    // 3.1. Create an instance of the SumArray class
    SumArray obj1 = new SumArray();

    // 3.2. Declare an array of integers
    int[] A = { 5, 3, 8, 10, 2, 1, 2, 3 };

    // 3.3. Call the method for calculating the sum of array elements
    // with a lambda expression that selects only paired elements
    int sum = obj1.Sum(A, ref1);
    System.out.println("sum = " + sum);

    // 3.4. Passing a lambda expression to a method for unpaired elements
    sum = obj1.Sum(A,
    (Odd)((d) -> {
      if (d%2!=0) return true;
      else return false;
    }));
    System.out.println("sum = " + sum);
  }
}

The result of the program

sum = 22
sum = 12

In the above code, you can use a method reference by example:

instance_name::method_name

In the program, passing the IsOdd() method to the Sum() method will look like this:

// also works - a reference to the instance method is passed
int sum = obj1.Sum(A, ref1::IsOdd);

 

5.3. Lambda expression using pattern (generic)

Create and invoke a lambda expression using a template (generic). The lambda expression must match at most three digits.

package TrainLambda;

// Generalized (template) functional interface.
// Contains a method that takes 3 numbers of type T
// and returns a value of type T.
interface Max3<T> {
  T Max(T a, T b, T c);
}

public class TrainLambda02 {

  public static void main(String[] args) {
    // Using the Max3<T> interface
    // 1. Declare the reference to Max3<T> interface
    Max3<Integer> refInt;

    // 2. Create a lambda expression with reference to int
    refInt = (a, b, c) -> {
      Integer max = a;
      if (max<b) max=b;
      if (max<c) max=c;
      return max;
    };

    // 3. Invoke lambda expression for three integers
    int res = refInt.Max(5, 7, 3); // res = 7
    System.out.println("res = " + res);

    // 4. Create lambda-expression for double type
    Max3<Double> refDouble;
    refDouble = (a, b, c) -> {
      Double max = a;
      if (max<b) max=b;
      if (max<c) max=c;
      return max;
    };

    // 5. Call the lambda expression with reference to the double type
    double resMax = refDouble.Max(3.88, 2.55, 4.11);
    System.out.println("resMax = " + resMax);
  }
}

 

5.4. Solving a quadratic equation using a lambda expression

The task demonstrates the use of a lambda expression to solve a quadratic equation. The program contains the following components:

  • functional interface Equation. The interface declares one method that receives three double parameters. These parameters are the coefficients a, b, c of the quadratic equation. The method returns the value of the roots of the equation as an instance of the Solution class. If the equation has no solution, then the method returns null;
  • Solution class – contains two fields x1, x2 of type double, which are the roots of a quadratic equation;
  • the TrainLambda02 class containing the main() function. This function builds and uses a lambda expression to solve a quadratic equation for the values a = 2, b = 8, c = 4.

The program text is as follows:

// A functional interface for solving a quadratic equation.
interface Equation {
  // The method receives the parameters - coefficients of equation a*x^2 + b*x + c = 0.
  // If the equation has no solution, then the method returns null.
  // Otherwise, the method returns an instance of the Solution class
  // with roots x1, x2.
  Solution CalcEquation(double a, double b, double c);
}

// The result of solving a quadratic equation
class Solution {
  double x1;
  double x2;
}

public class TrainLambda02 {

  public static void main(String[] args) {
    // Lambda expression that solves a quadratic equation.
    // The result is returned in an instance of the Solution class.
    // 1. Declare a reference to the functional interface
    Equation eq;

    // 2. Assign a lambda expression that solves a quadratic equation
    //   to the reference
    eq = (double a, double b, double c) -> {
      double d = b*b - 4*a*c; // discriminant
      if (d>=0) {
        // Find roots
        Solution roots = new Solution();
        roots.x1 = (-b-Math.sqrt(d))/(2*a);
        roots.x2 = (-b+Math.sqrt(d))/(2*a);
        return roots;
      }
      else
        return null;
    };

    // 3. Solve the quadratic equation 2*x^2 - 8*x + 4 = 0
    Solution roots = eq.CalcEquation(2, -8, 4);
    if (roots==null)
      System.out.println("The solution has no roots.");
    else
    {
      System.out.println("x1 = " + roots.x1);
      System.out.println("x2 = " + roots.x2);
    }
  }
}

The result of the program

x1 = 0.5857864376269049
x2 = 3.414213562373095

 


Related topics