lambda expression, you're the big guy after reading it!

The previous article introduced two common functional interfaces: Supplier and Consumer. Today, I will talk about another important functional interface, Predicate and Function. These interfaces are widely used in Java, such as Stream API.

2. Predict interface

When we need to judge a certain data type and get a boolean value result, we can use java.util.function . predict & lt; T & gt; interface. For example, to judge whether the List passed in contains a specific character PHP.

2.1 condition judgment

Sometimes we need to make business judgment. Suppose I want to implement a function input parameter as List. If the List parameter meets a certain condition, the corresponding result will be output. For example, the following code, checkchar(), will output if the input parameter list1 meets a certain condition System.out.println("meet corresponding conditions"), the specific conditions are uncertain, which may be to determine whether the List contains an element and whether the size meets the requirements. It is implemented by the caller.

private static boolean checkChar(List list1, Predicate<List> predicateList) {
        boolean rs = predicateList.test(list1);
        if (rs) {
            System.out.println("Meet the corresponding conditions");
        }
        return rs;
    }

Next, we implement the main method to call the checkchar() method above, and use the lambda expression to implement the specific judgment conditions inside.

public static void main(String[] args) {
        List list1 = new ArrayList();
        list1.add("PHP");
        list1.add("JAVA");
        list1.add("C++");
        boolean rs = checkChar(list1, s -> {
            return s.contains("PHP");
        });

        if (rs) {
            System.out.println("list1 Contains PHP character");
        } else {
            System.out.println("list1 Not included in PHP character");
        }
    }

In the main method, when the checkChar() method is called, the lambda expression s - & gt; {return s.contains ("PHP")} is passed in for judgment. This means that when the checkChar method is called, if the list1 list contains PHP, this element will execute System.out.println("meet corresponding conditions") statement.

The execution result of the above code is:

Meet the corresponding conditions
 PHP characters in list1

2.2 and judgment

Above we said single condition judgment. What if we want multiple condition judgments? You can use the and() method of Predicate.

Let's define a function first. In this function, there will be two judgments. The specific logic of the two judgments will be passed from the outside.

private static boolean checkChar2(List list1, Predicate<List> pre, Predicate<List> other) {
        return pre.and(other).test(list1);
}

In the main method, two judgment logic s - & gt; {return s.contains ("PHP");} and S - & gt; {return s.contains ("C + +);} are passed in, and then Predicate.and() method execution and operation.

public static void main(String[] args) {
        List list1 = new ArrayList();
        list1.add("PHP");
        list1.add("JAVA");
        list1.add("C++");
        boolean rs = checkChar2(list1, s -> {
            return s.contains("PHP");
        }, s -> {
            return s.contains("C++");
        });

        if (rs) {
            System.out.println("list1 At the same time PHP and C++character");
        } else {
            System.out.println("list1 Not satisfied at the same time PHP and C++character");
        }
    }

The execution result of the above code is:

list1 has both PHP and C + + characters

2.3 non judgment

"And", "or" we have learned. There is still a "non" (reverse) operation left. Let's see how to use the negate operation.

The gate() method of Predicate is as follows:

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

From the source code, we can see that after executing the test() method, the result boolean value is taken instead. Note that you must first call the negate method and then call the test method, which is the same as the and and or methods:

private static boolean checkNotChar(List list1, Predicate<List> pre1) {
        return pre1.negate().test(list1);
}

public static void main(String[] args) {
        List list1 = new ArrayList();
        list1.add("PHP");
        list1.add("JAVA");
        list1.add("C++");
        boolean rs = checkNotChar(list1, s -> {
            return s.contains("C#");
        });

        if (rs) {
            System.out.println("list1 No, C#");
        } else {
            System.out.println("list1 Yes C#");
        }
}

The execution result of the above code is:

list1 has no C#

3. Function interface

java.util.function . function & lt; t, R & gt; is equivalent to a function in data. Data of one type is used as input to get the output of data of another type.

3.1 conversion method: apply

In Function, the abstract method r apply (t t t) is used to perform the conversion. The method obtains the result of type R according to the data of parameter T type. For example, I want to convert String type to Integer type. The specific conversion is determined by the caller through lambda.

Let's define the transformation method first:

private static Integer transfer(String param, Function<String, Integer> function) {
        int num = function.apply(param);
        return num;
}

The specific transformation logic is defined in the main method:

public static void main(String[] args) {
        String str = "99";
        int num = transfer(str, s -> (Integer.parseInt(s) + 1));//The specific transformation logic is defined here
        System.out.println("DemoFunc The execution result is" + num);
}

3.2 cascading Transformation: andThen

If we want to do multi-step transformation, we need to use the andThen() method. The implementation of these specific transformations is implemented through lambda.

First, I define a chainTransfer method of cascading transformation, in which three functions are passed through one, two and three for transformation, and then the result is returned.

private static Integer chainTransfer(String str, Function<String, Integer> one, Function<Integer, Integer> two,
        Function<Integer, Integer> three) {
        int num = one.andThen(two).andThen(three).apply(str);
        return num;
    }

Next, we implement 3 Function in the main method and call the transformation function chainTransfer:

  public static void main(String[] args) {
        int num = chainTransfer("9", str -> Integer.parseInt(str) + 10, 
                                i -> i *= 10, i -> i + 5);
        System.out.println("The result after conversion is:" + num);
    }

The result of executing the main method above is:

The result is 195 

The specific conversion steps are as follows:

1. First convert 9 to integer type and then + 10, corresponding to Integer.parseInt(str) + 10, 19;
2. Then multiply by 10 to get 19 * 10 = 190;
3. Add 5 to the third part to get 190 + 5 = 195.

Of course, when defining this method, we should pay attention to that if the Function parameter type that depends on the call is suitable, for example, Function & lt; String, Integer & gt; one input parameter is String and the return value is Integer, then the input parameter of Function two must be Integer. That is, the return value of the previous Function should be compatible with the input parameter type of the next Function.

3.2 discussion on type compatibility in cascading transformation

As we said earlier, the return value of the previous Function should be compatible with the input parameter type of the next Function. Because the former Function is the input parameter of the latter Function, it is compatible when the actual input parameter is a subclass of the parameter in the Function call.

The following conversion functions are compatible:

 private static User objChainTransfer(String str, Function<String, VipUser> one, Function<User, User> two) {
        User u = one.andThen(two).apply(str);
        return u;
    }
}

The previous Function returns VipUser, which is a subclass of the input User of the second Function. We call the Function above in the main Function. The code is as follows:

public static void main(String[] args) {
        User u = objChainTransfer("19", str -> {
            return new VipUser("name" + str, str);
        }, vipUser -> {
            vipUser.setName(vipUser.getName() + "_new");
            return vipUser;
        });

        System.out.println("The result after conversion is:" + u);
}

After operation, the results are as follows:

The result after conversion is: VipUser{name='name19_new', id='19'} 

This code does the following:

1. Let's create a 'VipUser' object first
 2. Modify the name attribute,
3. Return the User object.

Further, we can analyze the source code:

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

In the source code, Function & lt;? Super R,? Extends V & gt; after in the andThen method indicates that the input parameter of this Function of after must be a super class of R, where R is the return value of the first Function.

You can view the details Function.java Source code:

public interface Function<T, R> {
    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
    //...
}

Tags: Java PHP Lambda Attribute

Posted on Sat, 20 Jun 2020 05:01:39 -0400 by sarabjit