What is functional programming

Functional programming is a programming paradigm with a high degree of abstraction. Functions written in pure functional programming languages have no variables. Therefore, as long as the input of any function is determined, the output is determined, and this pure function is called no side effects. While allowing the use of variables in programming languages, because of the variable state of the function is uncertain, the same input, may get different output, therefore, this function is a side effect. One of the great things about functional programming is that you can pass a function as an argument to another function, and you can return a function! Functional programming was first studied by mathematician Alonzo Church as a set of functional transformation logic, also known as Lambda Calculus (λ-Calculus), so it is often called Lambda calculation.

Java8 has some common method interfaces built inFunctionalInterface

This interface only defines an abstract method, and use the @ FunctionalInterface annotations markers, such as Predicate, Consumer, Function, : Supplier, Comparator and so on, these all belong to Java. Util. Function package

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}
// I'm not going to do it
Copy the code

They are characterized by defining the input parameter and return value of the function. When used, the expression that meets the definition of the function interface can be checked by the compiler. The following will introduce the function interface and the corresponding four ways of using it

As an example to see the difference between using a function and not using one, the requirement is to have a function and pass in oneList<Integer>, pick out the odd number, and the other pick out the even number, so let’s not write it in a functional way

    // Select a singular method
    public static List<Integer> filterSingular(List<Integer> list) {
        List<Integer> result = new ArrayList<>();
        for (Integer item : list) {
            if (item % 2! =0) { result.add(item); }}return result;
    }


    // Filter out even methods
    public static List<Integer> filterEven(List<Integer> list) {
        List<Integer> result = new ArrayList<>();
        for (Integer item : list) {
            if (item % 2= =0) { result.add(item); }}return result;
    }
Copy the code

After the method is defined, the expected output is [1,3,5,7] and [2,4,5].

        List<Integer> targetList = new ArrayList<Integer>() {
            {
                this.add(1);
                this.add(2);
                this.add(3);
                this.add(4);
                this.add(5);
                this.add(6);
                this.add(7); }}; List<Integer> singularList = filterSingular(targetList); List<Integer> evenList = filterEven(targetList); System.out.println(singularList); System.out.println(evenList);Copy the code

But the only difference between these two filtering functions is the condition, so you can abstract that condition and write it as a function interface, and the test definition for the Predicate interface is at the beginning of this article, and you pass in a generic type, and you return a Boolean, so rewrite the code for filter

    public static List<Integer> filter(List<Integer> list,Predicate<Integer> predicate) {
        List<Integer> result = new ArrayList<>();
        for (Integer item : list) {
            if(predicate.test(item)) { result.add(item); }}return result;
    }
Copy the code

In addition to passing the List, you can also pass an instance object that implements the Predicate interface. You only need to pass in and out parameters that meet the requirements of the function definition to compile. Here are four ways to use this function

  1. Use the traditional anonymous inner class injava8It was the only way to do it before
        List<Integer> singularList = filter(targetList, new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                return integer % 2! =0; }}); System.out.println(singularList);Copy the code
  1. uselambdaThe expression format is as follows() - > {}.(a)Is the list of methods,->{} is the body of the method. Since there is currently only one parameter and the parameter type can be inferred, the type and(a)I don’t have to write it, it’s just a method body,{}It is not recommended to write too long code in the method body. Readability should be ensured
        List<Integer> singularList2 = filter(targetList, integer -> integer % 2! =0);
        //
        // List<Integer> singularList3 = filter(targetList, (Integer integer) -> {
        // return integer % 2 ! = 0;
        // });
Copy the code

Lambda expressions can be used for reasons that satisfy abstract operations passed in Integer to return a Boolean, which can be automatically converted to function interfaces

  1. Static method references, where a static method is defined, can also be automatically converted to a function interface, using double colon syntax
    private static boolean integerWithSingular (Integer haha){
        return haha % 2! =0;
    }
Copy the code

Using static method references, where Cn is the name of the class, makes it even more readable than lambda expressions, because the method has a name, which can be used to determine what operation is being performed, and makes it easier to write more logic

    List<Integer> singularList3 = filter(targetList, Cn::integerWithSingular);
Copy the code
  1. Instance method, because the first argument to any instance method is always a hidden pointerthisPoints to the current instance, as passed in by the example generics aboveIntegerType, which needs to be overridden to demonstrate, declares a class first, and has an instance method that is passed inTestType returnsbooleanThe mapping of
public class Test {
    private long id;
    
    public Test(long id) {
        this.id = id;
    }
    
    private boolean integerWithSingular(a){
        return this.id % 2! =0; }}Copy the code

Change the Integer types of the filter function to all Test types

    public static List<Test> filter(List<Test> list, Predicate<Test> predicate) {
        List<Test> result = new ArrayList<>();
        for (Test item : list) {
            if(predicate.test(item)) { result.add(item); }}return result;
    }
Copy the code

In the following calls, passing in the class name :: instance method name achieves the equivalent effect

    ArrayList<Test> targetList = new ArrayList<Test>() {
        {
            this.add(new Test(1));
            this.add(new Test(2)); }}; filter(targetList,Test::integerWithSingular);Copy the code

Any interface that contains only one abstract method can be automatically converted to a function interface, even if you define your own interface without the @functionalInterface annotation

Use more function interfaces

  • ConsumerIf you put in an object, the output is empty, which is equivalent to consuming the incoming object,ArrayListtheforEachThe method usesConsumer
    // ArrayList forEach method source
    @Override
    public void forEach(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        final int expectedModCount = modCount;
        @SuppressWarnings("unchecked")
        final E[] elementData = (E[]) this.elementData;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            action.accept(elementData[i]);
        }
        if(modCount ! = expectedModCount) {throw newConcurrentModificationException(); }}Copy the code
  • FunctionMore closely related to the definition of a function, used to convert one type to another, such as a function in mathematics to convert X to Y, the definition of the function interface is as follows, again written as beforeTestClass to understand, write another onemapmethods
    public static String map(Test test, Function<Test, String> function) {
        return function.apply(test);
    }
Copy the code

Anything that returns a String that meets the requirements of passing a Test can be automatically converted

        map(new Test(1),test -> "name");
        
        // If the Test type also has a String name and the corresponding getter, it can be written as an instance method reference
        // map(new Test(2), Test::getName);
Copy the code
  • SupplierAs opposed to Consumer, the Consumer consumes, and the Supplier provides, providing one thing from the void
    public static Object create(Supplier<Object> supplier){
        return supplier.get();
    }
Copy the code

As long as the conditions are met for something to appear out of thin air

    create(Object::new);
    // The function of new is to create an object out of nothingness
    create(() -> "supplier");
    create(() -> new Test(1));
Copy the code

Finally, the use of functional programming in sorting is introduced

    // Collections.sort's static method definition
    public static <T> void sort(List<T> list, Comparator<? super T> c) {
        list.sort(c);
    }

    // static method definition for Comparator.comparing
    // we need to pass in a T type mapping to U
    The Comparable example is to pass a Test and return an object that implements the Comparable interface (e.g. Integer,String...).
    public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
    }

Copy the code

Here’s the refresher time

    // It is possible to sort by a field in an object with short code
    public static void main(String[] args) {
        ArrayList<Test> tests = new ArrayList<Test>() {
            {
                this.add(new Test(2."abc"));
                this.add(new Test(1."efg")); }};// Now the id field of the Test instance is sorted, then the array is reversed, and then the name field is sorted
        Collections.sort(tests, Comparator.comparing(Test::getId)
                .reversed()
                .thenComparing(Test::getName));
        System.out.println(tests);
    }
Copy the code

Other functions of the interface will not be described, as long as understand the principle, you can easily start to use