[Java] functional interface

Chapter I functional interface

1.1 concept

Functional interface in java refers to an interface with only one abstract method

Functional interface, that is, the interface suitable for functional programming scenarios. The embodiment of functional programming in java is Lambda, so functional interface is the interface that can be used by Lambda. Only by ensuring that there is only one abstract method in the interface can Lambda in java be deduced smoothly.

Note: "syntax sugar" refers to the code syntax that is more convenient to use, but the principle remains the same. For example, when traversing a collection, the underlying implementation principle of the for each syntax is still the iterator, which is the "syntax sugar". From the application level, lambda in java can be regarded as the "syntax sugar" of anonymous inner classes, but they are different in principle (anonymous inner classes will generate new class files when compiling, but lambda will not).

1.2 format

Just make sure there is only one abstract method in the interface:

Modifier  interface Interface name{
    public abstract Return value type method name(Optional parameter information); //The preceding modifier can be omitted
    //Content of other non abstract methods (default, static, private)
}

1.3 @FunctionalInterface annotation

Similar to the @ override annotation, Java 8 introduces a new annotation @ FunctionalInterface. For functional interfaces This annotation can be used for the definition of an interface:

@FunctionalInterface
public interface YourFather {
    void method();//An abstract method is defined
    
}

Function: it will automatically detect whether your interface conforms to the definition of functional interface. If it does not (there is no abstract method or more than one in the interface), the annotation will report an error

1.4 user defined functional interface and use

Custom functional interface

@FunctionalInterface
public interface YourFather {
    void method();//An abstract method is defined

}

Implementation class of interface

public class YourFatherC implements YourFather {
    @Override
    public void method() {
        System.out.println("I'm your father");
    }
}

use

  1. Can be used as a parameter of a method
public class Main {
    //As a parameter
    public static void show(YourFather yourFather){
        yourFather.method();
    }

    public static void main(String[] args) {
        //Call the show method. The parameter of the method is an interface, so the implementation class object of the interface can be passed
        show(new YourFatherC());

        //Anonymous Inner Class 
        show(new YourFather() {
            @Override
            public void method() {
                System.out.println("I'm your father");
            }
        });

        //You can use Lambda
        show(()-> System.out.println("I'm your father"));
    }
}

Chapter II functional programming

2.1 delayed execution of lambda

After the code of some scenarios is executed, the results may not be used, resulting in a waste of line energy Lambda expressions are delayed, which can be used as a solution to improve performance

Log cases of performance waste

Note: logs can help us quickly locate problems and record the situation during the operation of the program, so as to facilitate the monitoring and optimization of the project

A typical scenario is to conditionally use parameters. For example, after splicing log messages, print out when the conditions are met:

package demo09;


//Log case
public class Main {
    //Define a method to display log information according to the level of the log
    public static void showLog(int level, String message) {
        //Judge the level of the log
        if (level == 1) {
            System.out.println(message);
        }
    }

    public static void main(String[] args) {
        //We define three log information in the main method
        String msg1 = "I'm your father";
        String msg2 = "World";
        String msg3 = "Java";

        //Call showLo method and pass the write parameter
        showLog(2, msg1 + msg2 + msg3);
    }
}

We will find that there are some problems that can be wasted

  1. He should first splice the second parameter and judge the level
  2. If the level does not match, then the splicing is wasted

So We use Lambda expression to optimize this case (optimization premise: there must be a functional interface)

//Functional interface

package demo09;

@FunctionalInterface
public interface MessageBuilder {
    //Define a method to splice strings
    String stringBuilder();
}
//Main function

package demo09;
//Lambda features: delayed loading

//Premise of use: there must be a functional interface

public class Lambda {
    //Define a method to display logs
    public static void showLog(int level, MessageBuilder mb) {
        //Judge the level of the log
        if (level == 1) {
            System.out.println(mb.stringBuilder());
        }
    }

    public static void main(String[] args) {
        //We define three log information in the main method
        String msg1 = "I'm your father";
        String msg2 = "World";
        String msg3 = "Java";

        //Call showLo method and pass the write parameter
        showLog(2, () -> (msg1 + msg2 + msg3));


    }
}

After using Lambda, the Lambda expression is passed as a parameter, just passing the parameter to the showLog method

  • When the conditions are met, the rewritten method in Lambda will be called for string splicing
  • If the conditions are not met, the rewritten method will not execute and will not waste performance

2.2 use Lambda as parameter and return value

Regardless of the implementation principle, Lambda expressions in Java can be regarded as an alternative to anonymous inner classes If the parameter of a method is a functional interface type, you can use Lambda expression as the method parameter. In fact, you can use functional interface as the method parameter

I used this method in the last section. Just have a look

Similarly, if the return value type of a method is a functional interface, you can directly return a Lambda expression When you need a method to get a Java util. When an object of comparator interface type is used as a sorter, this method can be called to obtain

package demo09;


import java.util.Arrays;
import java.util.Comparator;

public class Lambda {
    private static Comparator<String> newComparator() {
        return (a,b) -> b.length() - a.length();//In ascending order, the return value is int
    }

    public static void main(String[] args) {
        String[] arr = {"Your father","Your father","Your grandfather"};
        System.out.println(Arrays.toString(arr));

        Arrays.sort(arr,newComparator());

        System.out.println(Arrays.toString(arr));
    }
}

Chapter III common functional interfaces

JDK provides a large number of commonly used functional interfaces to enrich the typical usage scenarios of Lambda, mainly in Java util. Function package is provided

3.1 Supplier interface

java. util. function. The supplier < T > interface only contains a parameterless method T get() to obtain object data of the type specified by a generic parameter. Because this is a functional interface, it means that the corresponding Lambda expression needs to "provide" an object data conforming to the generic type

package demo09;

// The supplier < T > interface is called a production interface. It specifies what type the generic type of the interface is, and what type the get() method in the interface will return

import java.util.function.Supplier;

public class Lambda {
    public static void method(Supplier<String> sup){
        System.out.println(sup.get());//The return value type of get is determined by the generic type of the interface parameter in the method parameter
    }

    public static void main(String[] args) {
        method(() -> "I'm your father");
    }
}

3.2 exercise: find the maximum value of array elements

Title:

Using the Supplier interface as the method parameter type, find the maximum value in the int array through the Lambda expression Tip: please use Java. Com for interface generics Lang. integer class

answer

package demo09;

// The supplier < T > interface is called a production interface. It specifies the type of the generic type of the interface, and the type returned by the get() method in the interface

import java.util.function.Supplier;

public class Lambda {
    public static int getMax(int[] arr,Supplier<Integer> sup){
        return sup.get();
    }

    public static void main(String[] args) {
        int[] arr = {123,444,2,66,8565};

        int maxNum = getMax(arr,() -> {
            int max = arr[0];
            for (int i : arr) {
                if (max < i){
                    max = i;
                }
            }
            return max;
        });

        System.out.println(maxNum);
    }
}

3.3 Consumer interface

java. util. function. The consumer < T > interface is just opposite to the Supplier interface. Instead of generating a data, it consumes a data. The data type is determined by generics

How to consume specifically needs to be customized (output, calculation, etc.)

Abstract method: accept

The Consumer interface contains the abstract method void accept (T), which means to consume data of a specified generic type Basic usage, such as:

package demo09;


import java.util.function.Consumer;

public class Lambda {
    private static void method(String name, Consumer<String> con){
        con.accept(name);

    }

    public static void main(String[] args) {
        method("Sweet shit",(name) -> {
            String newName = new StringBuilder(name).reverse().toString();
            System.out.println(newName);
        });//Output print result: excrement is really fragrant
    }
}

Default method: andThen

If the parameters and return values of a method are of Consumer type, the effect can be achieved: when consuming data, first do an operation, and then do an operation to realize combination

This method is the default method andThen in the Consumer interface The following is the JDK source code

default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };//This is the lambda format of the accept method
    }

Note: objects The requirenonnull () method detects whether the parameter is null. If so, it will actively throw a NullPointerException exception This eliminates the trouble of writing and throwing null pointer exceptions

To realize composition, you need two or more Lambda expressions. The semantics of andThen is a step-by-step operation For example, when two steps are combined

package demo09;


import java.util.function.Consumer;

public class Lambda {
    private static void method(String name, Consumer<String> con1, Consumer<String> con2) {
        //       con1.accept(name);
		//       con2.accept(name);
        con1.andThen(con2).accept(name);//This sentence means the above two sentences
    }
    public static void main(String[] args) {
        method("Hello World" , (name) -> {
            System.out.println(name.toLowerCase());
        }, (name) -> System.out.println(name.toUpperCase()));
    }
}

3.4 format and print information

subject

There are multiple pieces of information in the following string array. Please print the information in the format of "Name: XX gender: XX" It is required to take the action of printing name as the Lambda instance of the first consumer interface and the action of printing gender and amount as the Lambda instance of the second consumer interface, and splice the two interfaces together in order

public static void main (String[] args) {
    String[] array = {"Delireba,female","Gulinaza,female","Ma erzaha,male"};
}

answer

package demo09;


import java.util.function.Consumer;

public class Lambda {
    private  static void printInfo(String[] arr, Consumer<String> con1, Consumer<String> con2){
        for (String s : arr) {
            con1.andThen(con2).accept(s);
        }
    }

    public static void main(String[] args) {
        String[] array = {"Delireba,female", "Gulinaza,female", "Ma erzaha,male"};

        printInfo(array,(s) -> {
            String name = s.split(",")[0];
            System.out.println("full name: " + name);
        },(s) ->{
            String sex = s.split(",")[1];
            System.out.println("Gender: " + sex);
        });//Note that lambda's delayed execution method is used here. First, he executes the for statement in printInfo, and then executes the rewritten accept() method. Therefore, it is possible for lambda to write an s to represent a string, which can be recognized by the compiler
    }
//Operation results
//    Name: dirieba
//    Gender: Female
//    Name: gulinaza
//    Gender: Female
//    Name: Ma erzaha
//    Gender: Male
}

3.5 predict interface

Sometimes we need to judge the type of data to get a boolean result You can use Java util,function. Predict < T > interface

Abstract method: test

An abstract method boolean test(T t) contained in the predict interface is used to judge the scenario of conditions

package demo09;


import java.util.function.Predicate;

public class Lambda {

    private static boolean isLong(String length,Predicate<String> pre){
        return pre.test(length);
    }

    public static void main(String[] args) {
        String cjy = "88cm";

        String lxj = "99cm";

        boolean flag1 = isLong(cjy,(length) ->{
            if(Integer.parseInt(length.substring(0,2)) > 15){
                return true;
            }
            return false;
        });

        boolean flag2 = isLong(lxj,(length) ->{
            if(Integer.parseInt(length.substring(0,2)) > 18){
                return true;
            }
            return false;
        });

        System.out.println(flag1? "cjy Very long":"cjy Very short");
        System.out.println(flag2? "lxj Very long":"lxj Very short");
    }
}//The output results are long

Default method: and

Since it is conditional judgment, there will be three common logical relationships: and or not This method can be used when two predicate conditions are connected by "and" logic to achieve the effect of "and". Its source code is:

default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

Example:

package demo09;


import java.util.function.Predicate;

public class Lambda {

    private static boolean isBothLong(String length, Predicate<String> pre1, Predicate<String> pre2) {
        return pre1.and(pre2).test(length);
    }

    public static void main(String[] args) {
        String cjy = "18cm";

        String lxj = "88cm";

        boolean flag1 = isBothLong(cjy, (length) -> {
            if (Integer.parseInt(length.substring(0, 2)) > 15) {
                return true;
            }
            return false;
        }, (length) -> {
            if (Integer.parseInt(length.substring(0, 2)) > 15) {
                return true;
            }
            return false;
        });


        System.out.println(flag1 ? "cjy and lxj It's all very long" : "cjy and lxj It's all very short");


    }//The result is that it's all very long
}

Default method: or

Source code:

default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

The example is not done. The reason is the same as the former

Default method: negate

Source code

default Predicate<T> negate() {
        return (t) -> !test(t);
    }

It's just a negation, so there's no explanation

3.6 collection information filtering

subject

The information of multiple "name + gender" in the array is as follows. Please filter the qualified characters into the set ArrayList through the assembly of the predicate interface. Two conditions need to be met at the same time:

  1. Gender must be female

  2. The name is 4 characters

    Data:

    public static void main(String[] args) {
            String[] array = {"Delireba,female","Gulinaza,female","Ma erzaha,male"};
            
        }
    

answer

package demo09;


import java.util.ArrayList;
import java.util.function.Predicate;

public class Lambda {
    private static boolean isGirl(String s, Predicate<String> pre) {
        return pre.test(s);
    }

    public static void main(String[] args) {
        String[] array = {"Delireba,female", "Gulinaza,female", "Ma erzaha,male"};
        ArrayList<String> arr = new ArrayList<>();

        for (String s : array) {
            boolean flag1 = isGirl(s, (String str) -> str.split(",")[0].length() >= 4 && str.split(",")[1].equals("female"));
            if (flag1)  arr.add(s);
        }
        System.out.println(arr);
    }
//Operation results
    //[delireba, female, gulinaza, female]
}

3.7 Function interface

java. util. function. The function < T, R > interface is used to get another type of data from one type of data. The former is called pre event and the latter is called post condition

Abstract method: apply

The main abstract methods in the Function interface are: R apply(T t),

Example: convert String to Integer

package demo09;


import java.util.function.Function;

public class Lambda {
    //Convert string to integer
    public static void change(String s, Function<String, Integer> fun) {
        int in = fun.apply(s); //Automatic unpacking
        System.out.println(in);
    }
    public static void main(String[] args) {
        change("123", (str) -> Integer.parseInt(str));
    }
}

Default method: andThen

Source code:

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

It is the idea of chain programming, which saves the amount of code, but reduces the readability

For example: first convert the input String into Integer, and then multiply this Integer by 10

package demo09;


import java.util.function.Function;

public class Lambda {
    //Convert string to integer
    public static void method(String s, Function<String, Integer> fun1,Function<Integer, Integer> fun2) {
        System.out.println(fun1.andThen(fun2).apply(s));//Equivalent to fun2 apply(fun1.apply());
    }

    public static void main(String[] args) {
        method("123", (str) -> Integer.parseInt(str),(Int) -> Int*10);
    }
}

Incidentally, the method to quickly convert Integer to String is Integer + ""

3.8 exercise: splicing of custom function models

subject

Please use Function to splice Function models. Multiple Function operations to be performed in order are:

Example: String str = "Zhao Liying, 20";

  1. Intercept the age part of the number from String to get the String
  2. Converts a string to a number of type int
  3. Accumulate the int numbers in the previous step by 100 to get a String result

Problem solution

package demo09;


import java.util.function.Function;

public class Lambda {
    //Convert string to integer
    public static void method(String s, Function<String, String> fun1, Function<String, Integer> fun2, Function<Integer, String> fun3) {
        System.out.println(fun1.andThen(fun2).andThen(fun3).apply(s));//Equivalent to fun2 apply(fun1.apply());
    }

    public static void main(String[] args) {
        method("Zhao Liying,20", (str) -> str.split(",")[1], Integer::parseInt, (num) -> (num + 100) + "");
    }//Output result 120

}

Keywords: Java

Added by podja on Tue, 08 Mar 2022 23:41:19 +0200