Method reference of new features of JDK8

Keywords: Java

Method reference of new features of JDK8 (2)

Before looking at the method quotation, I recommend you to read what I wrote before Lambda expression of new features of JDK8 (I) , because method references are based on Lambda, it may be difficult to understand method references if you don't particularly understand functional interfaces and Lambda expressions.

Introduction to method reference

  • When the operation to be passed to the Lambda body already has an implemented method, you can use the method reference
  • Method reference can be regarded as the deep expression of Lambda expression. In other words, method reference is a Lambda expression, that is, an instance of functional interface. Pointing to a method by method name can be regarded as a syntax sugar of Lambda expression

Format of using method reference: using operator: "separate class (or object) from method name. There are three main uses:

  • Case 1: object:: instance method name
  • Case 2: Class:: static method name
  • Case 3: Class: instance method name
    Requirements for using method reference: the parameter list and return value type of the abstract method implementing the interface must be consistent with the parameter list and return value type of the method referenced by the method (only for case 1 and case 2)

Code example

Some of the above concepts may be abstract and difficult to understand. The following code examples show the usage of method references
Case 1: object:: instance method name
Customize a functional interface, FunctionInterface, which has only one abstract method, print method

// Annotations for declaring functional interfaces
@FunctionalInterface
public interface FunctionInterface {
    /**
     * Abstract method
     */
    public void print(String str);

}

We want to use the println () method of the PrintStream class in the print method (that is, System.out.println()) 😉
How to use lambda expressions:

  public static void main(String[] args) {
        FunctionInterface functionInterface = (str) -> {
            System.out.println(str);
        };
        functionInterface.print("Ha ha ha");
    }

We transformed the above lambda into a method reference. Here, we need to pay attention to whether the premise of using method reference mentioned earlier meets:

  1. There are already implemented methods on the right side of the Lambda body, that is to say, we can directly call the println method to achieve the purpose of printing, and this println method belongs to the implemented method and belongs to the PrintStream class
  2. The parameters and return values of the implemented method we call must be consistent with the abstract method we defined, that is, the print method parameter defined by ourselves is str and the return value is void, and such a method happens to exist in the println method

Method reference, syntax format, object:: instance, method name:

public static void main(String[] args) {
        // PrintStream object
        PrintStream printStream = System.out;
        //Object:: instance method name
        FunctionInterface functionInterface = printStream ::println;
        // The print method is called here. In fact, the println method is called through the method reference
        functionInterface.print("Ha ha ha");
    }

Case 2: Class:: static method name
The difference from the object:: instance method name in the first case is whether the method referenced in your lambda body (method body) is a static method or a non-static method. If the static method is a class:: static method name, and the non-static method is an object:: static method name. Similarly, we can show it through code. First, use the method of lambda expression

 public static void main(String[] args) {
        // Compare the size of two numbers by using Comparator
        Comparator<Integer> comparator = (num1, num2) -> Integer.compare(num1,num2);
        System.out.println(comparator.compare(2,3));
    }

Method reference is used. The syntax format is: Class:: static method name:

public static void main(String[] args) {
        /**
         * By using Comparator to compare the size of two numbers, since the compare method in Integer is a static method,
         * And the parameters and return values are the same as the compare method of Comparator, so you can use method reference
         * Because it is a static method, it can be called directly using the class name
         */
        Comparator<Integer> comparator = Integer::compare;
        System.out.println(comparator.compare(2,3));
    }

Case 3: Class: instance method name
Compared with the first two methods, this method may not be easy to understand, because the number of parameter lists in the method does not seem to match the number of parameters in the method referenced by the method. For example, the method int myCompare(T t1,T t2) in the user-defined functional interface and int s1.compareTo(s2) in the String class , it seems that the parameter list does not match, but the method reference can be used when t1 of myCompare can be used as the caller of the compareTo method in String and t2 of myCompare can be used as the parameter of the compareTo method in String. Let's first look at the implementation of Lambda expression:

public static void main(String[] args) {
        /**
         * The compareTo method in String is used to compare the sizes of two strings
         */
        Comparator<String> comparator = (s1,s2) -> s1.compareTo(s2);
        int result = comparator.compare("abcd", "abcf");
        System.out.println(result);
    }

Use method reference to implement, syntax format, class:: instance method name. In this case, we can understand that class (the class corresponding to the caller of the method):: instance method name (the specific method called). Let's see how to use it first

 public static void main(String[] args) {
        /**
         * Class:: instance method name
         * Class: String represents the class corresponding to the first parameter
         * Instance method name: the instance method called, and the parameters will be matched automatically
         */
        Comparator<String> comparator = String::compareTo;
        int result = comparator.compare("abcd", "abcf");
        System.out.println(result);
    }

This is really difficult to understand. It needs more practice to understand. Let's take another example to see the use of class:: instance method name
Customize a functional interface first:

@FunctionalInterface
public interface MyInterface {
    /**
     * Custom character replacement method
     * @param str String to replace
     * @param oldStr Original string
     * @param newStr New string
     * @return
     */
    public String myStrReplace(String str,String oldStr,String newStr);
}

We know that there is a String replacement method replace in the String class. First, we use Lambda expression to complete the implementation of myStrReplace method

public static void main(String[] args) {

        MyInterface myInterface  = (str,oldStr,newStr)->{
            return  str.replace(oldStr,newStr);
        };
        String str = "I am a doctor";
        String oldStr = "doctor";
        String newStr = "policeman";
        String result = myInterface.myStrReplace(str, oldStr, newStr);
        System.out.println(result);
    }

Method reference method:

 public static void main(String[] args) {
        /**
         * ::On the left is the class corresponding to the first parameter
         * ::On the right is the called method, and the parameters will be automatically corresponding
         */
        MyInterface myInterface  = String::replace;
        String str = "I am a doctor";
        String oldStr = "doctor";
        String newStr = "policeman";
        String result = myInterface.myStrReplace(str, oldStr, newStr);
        System.out.println(result);
    }

Constructor reference

Constructor reference is similar to method reference. The formal parameter list of the abstract method of the functional interface is consistent with that of the constructor. The return value type of the abstract method is the type of the class to which the constructor belongs
Customize a User class

public class User {
    private String name;
    private Integer age;

    public User() {
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

}

We want to obtain a User object with an initial value, and use the Supplier interface built in Jdk. This interface is introduced in the recommendation article at the beginning of the article. It is only used here. We will not introduce it. First use Lambda expression to complete it.

public static void main(String[] args) {
        Supplier<User> supplier = () -> new User();
        User user = supplier.get();
    }

The Supplier's get method is parameterless, and the User has a parameterless structure. The User object is obtained by method reference, which is implemented by constructor reference:

  public static void main(String[] args) {
        Supplier<User> supplier = User::new;
        User user = supplier.get();
    }

Take another example with parameters to enhance understanding. For example, if we want to obtain the user object with specified name and age, we should first create a functional interface

@FunctionalInterface
public interface UserFunctionalInterface {

    public User getUser(String name,Integer age);
}

Complete with a Lambda expression:

public static void main(String[] args) {
        UserFunctionalInterface userFunctionalInterface = (name, age) -> new User(name, age);
        User user = userFunctionalInterface.getUser("Zhang San", 23);
    }

Use constructor references to implement:

 public static void main(String[] args) {
        UserFunctionalInterface userFunctionalInterface = User::new;
        User user = userFunctionalInterface.getUser("Zhang San", 23);
    }

Array reference

You can treat the array as a special class, then the writing method is the same as the constructor reference

For example, to obtain an array of a specified size, we use the Function interface built in Jdk. This interface is not introduced. As described in the previous article, we use Lambda expression to complete it first

 public static void main(String[] args) {
        // Want a String array with a length of 5
        Function<Integer,String[]> function = (num) -> new String[num];
        String[] apply = function.apply(5);
        System.out.println(Arrays.toString(apply));
    }

Use method references to implement:

public static void main(String[] args) {
        // Want a String array with a length of 5
        Function<Integer, String[]> function = String[]::new;
        String[] apply = function.apply(5);
        System.out.println(Arrays.toString(apply));
    }

summary

Method references, constructor references and array references are all based on Lambda expressions. Method references greatly simplify the code and increase the difficulty of reading. It can be said that both Lambda expressions and method references are a kind of syntax. It is hard to avoid difficulties when we first contact them, just like when we first learned programming, So just practice getting used to this grammar.

Posted by bigsid on Sun, 19 Sep 2021 11:26:23 -0700