Detailed explanation of Lambda expression and functional interface

catalogue

1, Functional interface

1. Syntax:

2. Functional interface features:

3. Functional interface summary:

2, lambda

1.lambda preconditions:

2. Syntax of lambda expression:

3. Expression cases

4. There are three ways to write lambda expressions:

Expression single statement expression:

statement block:

Reference method reference:

5. Summary of main features of lambda expression:

3, Implementation cases of common lambda expressions

1. Implement Runnable with lambda expression

2. Use Java 8 lambda expression for event processing

3. Iterate over the list using lambda expressions

4. Use lambda expression and functional interface predict

5. How to add Predicate in lambda expression

6. Map and Reduce examples using lambda expressions in Java 8

6.1 example 1

6.2 example 2

7. Create a String list by filtering

8. Apply a function to each element of the list

9. Copy different values and create a sub list

10. Calculate the maximum, minimum, sum and average values of set elements

1, Functional interface

A functional interface with only one abstract method, except the public method of Object.

1. Syntax:

@FunctionalInterface
public interface Func{
    Object clone();   // Object method
    void run();       // Custom abstract method
}

2. Functional interface features:

  • The @ FunctionalInterface annotation is marked in the interface
  • Only one abstract method in the interface is automatically recognized as a functional interface by the compiler
  • There is an abstract method in the interface, and other abstract methods that contain the Object class will also be recognized as abstract interfaces

3. Functional interface summary:

nameUnary interfaceexplainBinary interfaceexplain
General functionFunctionUnary function, abstract apply methodBiFunctionBinary function, abstract apply method
Operator function (input and output of the same type)UnaryOperatorUnary operator, abstract apply methodBinaryOperatorBinary operator, abstract, apply method
Predicate function (output boolean)PredicateUnary predicate, abstract test methodBiPredicateBinary predicate, abstract test method
Consumer (no return value)ConsumerUnary consumer function, abstract accept methodBiConsumerBinary consumer, abstract accept method
Supplier (no parameters, only return value)SupplierProvider function, abstract get method--

2, lambda

1.lambda preconditions:

  • You must have a functional interface to use lambda expressions

2. Syntax of lambda expression:

(Parameter name) -> {Code block};    // Single line can not add {}, and multiple lines must add {}

LambdaParameters -> LambdaBody
args -> expr perhaps(object... args) -> {Functional interface abstract method implementation logic}
1.()The number of parameters is determined according to the number of abstract parameters in the functional interface. When there is only one parameter,()Can be omitted
2.When expr When the logic is very simple,{}and return Can be omitted

3. Expression cases

 ()->{}
​ ()->{System.out.println(1);}
​ ()->System.out.println(1)
​ ()->{return 100;}
​ ()->100
​ ()->null
​ (int x)->{return x+1;}
​ (int x)->x+1
​ (x)->x+1
​ x->x+1

4. There are three ways to write lambda expressions:

1) expression: single statement expression

2) statement: statement block

3) reference: method reference

Expression single statement expression:

There is no need to write the return keyword in the expression expression. The interpreter will automatically return the evaluation result of the expression.        

// Example: (parameter) - > expression

static <T> Function<T, T> identity() {
        return t -> t;  // here
}

statement block:

The statement statement block wraps multiple statements through braces {}. If it is an interface that needs to return results, it must be displayed, and return must be added to indicate the returned variables

// Example: (parameter) - > {}

// No return value:
public interface MyInterface{
    void test(String n, String c);
}

public static void testLambada1(){
    MyInterface myInterface = (String n,String c) -> {
        System.out.println(n);
        System.out.println(c);
    };
    myInterface.test("abc","123");
}

// With return value:
public interface MyInterface1{
    String test1(String n, String c);
}

public static void testLambada2(){
    MyInterface1 myInterface = (String n,String c) -> {
        System.out.println(n);
        System.out.println(c);
        return n + c;
    };
    System.out.println(myInterface.test1("abc","123"));
}

Reference method reference:

If a method is structurally matched with the corresponding method in the lambada expression, it can be directly referenced to the lambada expression, which contains a total of four reference types. The syntax format is shown in the table:

typegrammar
Instance based method referenceobject::methodName
Construction method referenceclassName::new
Parameter instance based method referenceclassName::methodName
Static method referenceclassName::staticMethodName

Instance based method reference (example):

package com.test.demo;

import java.util.function.Supplier;
import java.util.function.UnaryOperator;

/**
 * Created by xiaobaiaixuexi on 2022-1-10
 */
public class InstantMethodUse {

    String put() {
        return "hello";
    }

    public String toUpCase(String s) {
        return s.toUpperCase();
    }

    // Call the method of the current instance
    void test() {
        UnaryOperator<String> unaryOperator = this::toUpCase;
        System.out.println(unaryOperator.apply("this inst method"));

    }

    public static void main(String[] args) {
        Supplier<String> stringSupplier = () -> new InstantMethodUse().put();
        Supplier<String> stringSupplier1 = () -> {
            return new InstantMethodUse().put();
        };
        Supplier<String> stringSupplier2 = new InstantMethodUse()::put;
        System.out.println(stringSupplier2.get());
        //Common lambda expressions use
        Supplier<String> stringSupplier3 = () -> new InstantMethodUse().put();

        //Two ways to use
        UnaryOperator<String> unaryOperator = new InstantMethodUse()::toUpCase;
        //New object reference
        InstantMethodUse instantMethodUse = new InstantMethodUse();
        UnaryOperator<String> unaryOperator1 = instantMethodUse::toUpCase;
        System.out.println(unaryOperator.apply("lambda instMethod Use") + "  ===  " + unaryOperator1.apply("lambda instMethod Use"));

        instantMethodUse.test();
    }
}

Construction method reference (example):

// Construction method reference example:

public interface MyInterface{
    // Get all
    List<String> getAll();
}

public static void main(String[] args){
    MyInterface i = ArrayList:new;
}

Parameter based method reference (example):

// Parameter based method reference (example):

public interface MyInterface{
    // Compare it
    void compare(String str1, String str2);
}

pulbic static void main(String[] args){
    MyInterface i = String::compareTo;
}

Based on static method reference (example):

package com.test.demo;

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * Created by xiaobaiaixuexi on 2022-1-10
 */
public class StaticMethodUse {

    static <T> String hello(T s) {
        return s.toString();
    }
    static <T> String ret() {
        return "hello";
    }
    static void getSize(String s) {
        System.out.println(s.length());
    }
    static String toUpCase(String s){
        return s.toUpperCase();
    }
    public static void main(String[] args) {
        Supplier<String> supplier = () -> hello("hello");
        //Use the method to reference Supplier T get();
        // 1. Only output parameters are supported, not input parameters, so the following method reports an error
        //2. Why don't you need parentheses for a reference to a method? Because it is only a reference to a method, it is not executed
//        Supplier<String> stringSupplier = StaticMethodUse::hello("wqer");  //  report errors
        Supplier<String> s = StaticMethodUse::ret;
        Supplier<String> supplier1 = Fun::put;
        Function<Integer, String> function = StaticMethodUse::hello;
        //The parameters of the method must be passed in when calling, and the functional interface must be called
        System.out.println("function" + function.apply(123));

        //Use of ordinary lambda
        Consumer<String> consumer = size -> StaticMethodUse.getSize(size);
        //Why don't you need parentheses for a reference to a method? Because it is only a reference to a method, it is not executed
        Consumer<String> c1 = StaticMethodUse::getSize;
        c1.accept("hello use lambda");

        //Output parameter uppercase
        Function<String ,String> function1 = (ss) -> ss.toUpperCase();
        Function<String,String> function2 = StaticMethodUse::toUpCase;
        Function<String,String> function3 = Fun::toUpCase;
        System.out.println(function1.apply("aasdf"));
        System.out.println(function2.apply("aasdf"));
        System.out.println(function3.apply("aasdf"));
    }
}

class Fun {
    static String put() {
        return "hello";
    }
    static String toUpCase(String s){
        return s.toUpperCase();
    }
}

5. Summary of main features of lambda expression:

  • Optional type declaration: there is no need to declare parameter types, and the compiler can uniformly identify parameter values.
  • Optional parameter parentheses: one parameter does not need to define parentheses, but multiple parameters need to define parentheses.
  • Optional curly braces: if the topic contains a statement, curly braces are not required.
  • Optional return keyword: if the body has only one expression return value, the compiler will automatically return the value. Braces need to specify that the expression returns a value.

3, Implementation cases of common lambda expressions

1. Implement Runnable with lambda expression

// Before Java 8:
new Thread(new Runnable() {
    @Override
    public void run() {
    System.out.println("Before Java8, too much code for too little to do");
    }
}).start();

//Java 8 mode:
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();

Output:

too much code, for too little to do
Lambda expression rocks !!

2. Use Java 8 lambda expression for event processing

If you have used Swing API programming, you will remember how to write event listening code. This is another classic use case of simple anonymous classes in the old version, but it can not be so now. You can write better event listening code with lambda expression, as follows:

// Before Java 8:
JButton show =  new JButton("Show");
show.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
    System.out.println("Event handling without lambda expression is boring");
    }
});

// Java 8 mode:
show.addActionListener((e) -> {
    System.out.println("Light, Camera, Action !! Lambda expressions Rocks");
});

3. Iterate over the list using lambda expressions

// Before Java 8:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String feature : features) {
    System.out.println(feature);
}

// After Java 8:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
features.forEach(n -> System.out.println(n));
 
// It is more convenient to use the method reference of Java 8. The method reference is marked by:: Double colon operator,
// Looks like the scope resolution operator in C + +
features.forEach(System.out::println);

Output:

Lambdas
Default Method
Stream API
Date and Time API

4. Use lambda expression and functional interface predict

// In the following code, if an error is reported, you need to add the generic type
public static void main(args[]){
    List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
 
    System.out.println("Languages which starts with J :");
    filter(languages, (str)->str.startsWith("J"));
 
    System.out.println("Languages which ends with a ");
    filter(languages, (str)->str.endsWith("a"));
 
    System.out.println("Print all languages :");
    filter(languages, (str)->true);
 
    System.out.println("Print no language : ");
    filter(languages, (str)->false);
 
    System.out.println("Print language whose length greater than 4:");
    filter(languages, (str)->str.length() > 4);
}
 
public static void filter(List names, Predicate condition) {
    for(String name: names)  {
        if(condition.test(name)) {
            System.out.println(name + " ");
        }
    }
}

Output:

Languages which starts with J :
Java
Languages which ends with a
Java
Scala
Print all languages :
Java
Scala
C++
Haskell
Lisp
Print no language :
Print language whose length greater than 4:
Scala
Haskell
// Better way
public static void filter(List names, Predicate condition) {
    names.stream().filter((name) -> (condition.test(name))).forEach((name) -> {
        System.out.println(name + " ");
    });
}

5. How to add Predicate in lambda expression

java.util.function.Predicate allows two OR more predicates to be combined into one. It provides methods similar to the logical operators AND and AND OR, called and(), or(), AND xor(), to combine the conditions passed into the filter() method. For example, to get all four letter languages starting with J, you can define two independent predicate examples to represent each condition, AND then use predicate The and() method combines them as follows:

// You can even combine Predicate with and(), or() and xor() logical functions,
// For example, to find all four letter names starting with J, you can merge two predictions and pass them in
Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
names.stream()
    .filter(startsWithJ.and(fourLetterLong))
    .forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));

6. Map and Reduce examples using lambda expressions in Java 8

6.1 example 1

// Do not use lambda expressions to add 12% tax to each order
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
for (Integer cost : costBeforeTax) {
    double price = cost + .12*cost;
    System.out.println(price);
}
 
// Using lambda expressions
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
costBeforeTax.stream().map((cost) -> cost + .12*cost).forEach(System.out::println);

Output:

112.0
224.0
336.0
448.0
560.0
112.0
224.0
336.0
448.0
560.0

6.2 example 2

In the previous example, you can see that the map transforms collection class (such as list) elements. There is also a reduce() function that combines all values into one. Map and reduce operations are the core operations of functional programming. Because of their functions, reduce is also called folding operation. In addition, reduce is not a new operation. You may already be using it. Aggregate functions like sum(), avg(), or count() in SQL are actually reduce operations because they receive multiple values and return a value. The reduceh() function defined by the stream API can accept lambda expressions and merge all values. Classes such as IntStream have built-in methods such as average(), count(), sum() to perform the reduce operation, and mapToLong(), mapToDouble() to perform the conversion. This does not limit you. You can use the built-in method or define it yourself. In the Map Reduce example of Java 8, we first apply a 12% VAT to all prices, and then use the reduce() method to calculate the sum.

// Add 12% tax to each order
// Old method:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double total = 0;
for (Integer cost : costBeforeTax) {
    double price = cost + .12*cost;
    total = total + price;
}
System.out.println("Total : " + total);
 
// new method:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
System.out.println("Total : " + bill);

Output:

Total : 1680.0
Total : 1680.0

7. Create a String list by filtering

Filtering is a common operation of Java developers on large-scale collections, and now filtering large-scale data collections using lambda expressions and stream API s is surprisingly simple. The stream provides a filter() method that accepts a Predicate object, that is, you can pass in a lambda expression as the filtering logic. The following example is to filter Java collections with lambda expressions, which will help you understand.

// Create a list of strings, each with a length greater than 2
List<String> filtered = strList.stream().filter(x -> x.length()> 2).collect(Collectors.toList());
System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered);

Output:

Original List : [abc, , bcd, , defg, jk], filtered list : [abc, bcd, defg]

8. Apply a function to each element of the list

We usually need to use a function for each element of the list, such as multiplying by a number one by one, dividing by a number, or doing other operations. These operations are very suitable for the map() method. You can put the conversion logic in the map() method in the form of lambda expression, and then you can convert each element of the set, as shown below.

// Convert strings to uppercase and link them with commas
List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
System.out.println(G7Countries);

Output:

USA, JAPAN, FRANCE, GERMANY, ITALY, U.K., CANADA

9. Copy different values and create a sub list

This example shows how to use the distinct() method of the stream to de duplicate the collection.

// Create a square list with all the different numbers
List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
List<Integer> distinct = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
System.out.printf("Original List : %s,  Square Without duplicates : %s %n", numbers, distinct);

Output:

Original List : [9, 10, 3, 4, 7, 3, 4],  Square Without duplicates : [81, 100, 9, 16, 49]

10. Calculate the maximum, minimum, sum and average values of set elements

There is a very useful method called summaryStatistics() in the classes of streams such as IntStream, LongStream and DoubleStream. You can return IntSummaryStatistics, LongSummaryStatistics, or doublesummarystatistics s to describe various summary data of elements in the stream. In this example, we use this method to calculate the maximum and minimum values of the list. It also has getSum() and getAverage() methods to get the sum and average of all elements of the list.

//Get the number, minimum, maximum, sum and average of numbers
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest prime number in List : " + stats.getMax());
System.out.println("Lowest prime number in List : " + stats.getMin());
System.out.println("Sum of all prime numbers : " + stats.getSum());
System.out.println("Average of all prime numbers : " + stats.getAverage());

Output:

Highest prime number in List : 29
Lowest prime number in List : 2
Sum of all prime numbers : 129
Average of all prime numbers : 12.9

Author: Xiao Bai loves learning!!

Welcome to follow and forward comments and praise communication. Your support is the driving force of Xiaobai!

Keywords: Java Lambda

Added by Shawazi on Wed, 12 Jan 2022 12:51:43 +0200