Java8 Lambda expressions and Stream API: Lambda expressions

Reprinted from
This article mainly explains the Java8 Stream API, but to explain this part, you need the knowledge of anonymous inner classes, lambda expressions and functional interfaces. This article will be divided into two articles to explain the above contents, which readers can refer to as needed.

  • Java anonymous inner classes, lambda expressions and functional interfaces
  • Java Stream API

This article is the first in this series of blog posts on Java anonymous inner classes, lambda expressions and functional interfaces. It mainly explains Java anonymous inner classes, lambda expressions and functional interfaces. The second article on Java Stream API mainly explains Java Stream API.

Anonymous Inner Class
Anonymous internal classes are applicable to those classes that only need to be used once, such as the command mode in design mode. They are often called by defining a series of interfaces. Sometimes some commands will be executed only once and will not be executed again. At this time, if a class is defined separately, it will be too complex and the class will be generated by compilation Class file is not conducive to management. In this case, using anonymous internal classes can simplify programming and will not compile and generate separate classes Class file.

Let's take an example:

interface Programmer
{
    void listLanguages();
    void introduceMyself();
}

class MyProgrammer implements Programmer
{
    public void listLanguages() {
        System.out.println("Objective-C Swift Python Go Java");
    }

    public void introduceMyself() {
        System.out.println("My Name is Jiaming Chen");
    }
}

public class HelloWorld
{   
    static void info(Programmer programmer)
    {
        programmer.listLanguages();
        programmer.introduceMyself();
    }
    
    public static void main(String[] args)
    {   
        info(new MyProgrammer());
    }
}

In order to execute the info function, the above example defines a class MyProgrammer that implements the Programmer interface. If it is executed only once, it will be too complex. If anonymous inner classes are used, programming will be simplified to a great extent. First, let's introduce the basic syntax of anonymous inner classes:

new Interface to be implemented() | Parent constructor()
{
    //Methods that need to be implemented or methods that overload the parent class
}

The syntax of anonymous inner class is very simple. You must implement an interface or inherit a class. You can see that the new keyword is used. Therefore, when creating an anonymous inner class, an instance of the class will be created, and only one instance can be created. After creation, the anonymous inner class can no longer be used. Therefore, the anonymous inner class cannot be an abstract class, Since the anonymous inner class does not have a class name, the constructor cannot be defined, but the parameterized constructor of the parent class can be called when defining the anonymous inner class, and the initialization block can also be defined to initialize the member variables of the parent class. The following example is the implementation of modifying the above code to an anonymous inner class:

class MyProgrammer implements Programmer
{
    public void listLanguages() {
        System.out.println("Objective-C Swift Python Go Java");
    }

    public void introduceMyself() {
        System.out.println("My Name is Jiaming Chen");
    }
}

public class HelloWorld
{   
    static void info(Programmer programmer)
    {
        programmer.listLanguages();
        programmer.introduceMyself();
    }
    
    public static void main(String[] args)
    {   
        info(new Programmer(){
            public void listLanguages() {
                System.out.println("Objective-C Swift Python Go Java");
            }

            public void introduceMyself() {
                System.out.println("My Name is Jiaming Chen");
            }
        });
    }
}

Through comparison, it is found that it is more concise to redefine a new class using anonymous internal analogy. When creating an anonymous internal class, you can call the parameterized constructor of the parent class. Examples are as follows:

abstract class Programmer
{
    protected String name;
    
    public Programmer(String name)
    {
        this.name = name;
    }
    
    public abstract void listLanguages();
    public abstract void introduceMyself();
}

public class HelloWorld
{   
    static void info(Programmer programmer)
    {
        programmer.listLanguages();
        programmer.introduceMyself();
    }
    
    public static void main(String[] args)
    {   
        int age = 22;
        Programmer p = new Programmer("Jiaming Chen"){
            public void listLanguages()
            {
                System.out.println("Objective-C Swift Python Go Java");
            }
            public void introduceMyself()
            {
                System.out.println("My Name is " + this.name + " and I'm " + age + " years old.");
                //age = 2;
            }
        };
        info(p);
        //age = 23;
    }
}

The above example first defines an abstract parent class, and the abstract parent class has only one constructor. Therefore, when creating an anonymous inner class, it is necessary to call the constructor, so that the member variables defined by the parent class can be used inside the anonymous inner class, and the anonymous inner class can also use external variables, In Java 8, the age in the above chestnuts will be automatically declared as the final type, which is called effectively final. As long as the anonymous internal class accesses a local variable, the local variable will be automatically declared as the final type regardless of whether it is finally modified or not. It is not allowed to be modified anywhere. Compared with other languages, Java has greater limitations in accessing external variables in closures, Because it can only be of final type, for example, OC can also capture external variables inside the block, and swift also has a set of closure value capture mechanism, which can modify the captured values or set permissions. Java has too many limitations.

lambda expressions
Java 8 introduces lambda expressions. In other languages, such as python and swift, lambda expressions are supported. This feature is also very convenient and concise to use. Next, take a common example of sorting a list:

class MyComparator implements Comparator<String>
{
    public int compare(String o1, String o2)
    {
        return o1.compareTo(o2);
    }
}

public class HelloWorld
{   
    public static void main(String[] args)
    {   
        ArrayList<String> list = new ArrayList<>();
        list.add("Objective-C");
        list.add("Swift");
        list.add("Python");
        list.add("Golang");
        list.add("Java");
        list.sort(new MyComparator());
        list.forEach(System.out::println);
        /*
        Output: (the output results of the following codes are consistent and will not be repeated)
        Golang
        Java
        Objective-C
        Python
        Swift
        */
    }
}

Sorting operations are often performed on a collection type during development. When calling the sort method of a collection, a parameter that implements the Comparator interface needs to be passed in. Therefore, the above chestnut defines a class MyComparator and this class implements the Comparator interface. Therefore, an object of MyComparator can be created for sorting operations, It's not difficult to find that this is very complicated, and a new class needs to be redefined. After the introduction above, it can be replaced by anonymous internal class. About the last line of code list forEach(System.out::println); As will be introduced later, let's sell a key here. Its function is to traverse the whole set and output. Next, let's take a look at the implementation of anonymous inner classes:

public class HelloWorld
{   
    public static void main(String[] args)
    {   
        ArrayList<String> list = new ArrayList<>();
        list.add("Objective-C");
        list.add("Swift");
        list.add("Python");
        list.add("Golang");
        list.add("Java");
        list.sort(new Comparator<String>(){
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }           
        });
        list.forEach(System.out::println);
    }
}

The result is the same as above. Obviously, using anonymous inner classes is more convenient and the code is concise. Is there any way to introduce it? Of course, the answer is yes, that is to use lambda expressions. Examples are as follows:

public class HelloWorld
{   
    public static void main(String[] args)
    {   
        ArrayList<String> list = new ArrayList<>();
        list.add("Objective-C");
        list.add("Swift");
        list.add("Python");
        list.add("Golang");
        list.add("Java");
        list.sort((String o1, String o2)->{
            return o1.compareTo(o2);
        });
        list.forEach(System.out::println);
    }
}

Through the above code, it is found that the parameter passed in by sort function is a lambda expression. The whole code is very similar to the compare function we implemented in the previous article, but we don't need to write cumbersome code such as new comparator < string(), and we don't need to write the function name. Therefore, lambda expression can well replace anonymous inner classes, It is not difficult to find that the lambda expression consists of three parts:

The first is the parameter list, which is consistent with the parameter list of the method to be implemented. Because the system already knows which method you need to implement, you can omit the formal parameter type. When there is only one parameter, you can also omit the parentheses, but when there is no formal parameter, the parentheses cannot be omitted
Next is a - > symbol, which is used to separate the formal parameter list from the function body. This symbol cannot be omitted.
Finally, the code body. If there is only one line of code in the code body, the curly braces can be omitted. If the method needs to have a return value, even the return keyword can be omitted. The system will automatically return the result of this line of code.
Through the above explanation, we can write a more concise lambda expression. Examples are as follows:

public class HelloWorld
{   
    public static void main(String[] args)
    {   
        ArrayList<String> list = new ArrayList<>();
        list.add("Objective-C");
        list.add("Swift");
        list.add("Python");
        list.add("Golang");
        list.add("Java");
        list.sort((s1, s2)->s1.compareTo(s2));
        list.forEach(System.out::println);
    }
}

In the above code, we omit the type of formal parameter. Since there is only one line, we omit curly braces and return statement at the same time, the whole code is more concise than using anonymous inner classes. Here, some students may ask, how do lambda expressions know which method of the interface we implement?

The type of lambda expression is also called target type. This type must be a Functional Interface. A Functional Interface represents an interface with only one abstract method, but can contain multiple default methods or class methods. Therefore, the system using lambda expression must know which method of the interface we implement, Because the implemented interface has and has only one abstract method for us to implement.

Functional interfaces can use the annotation @ FunctionalInterface to require the compiler to check whether it contains only one abstract method at compile time. Java provides a large number of functional interfaces, which can simplify programming using lambda expressions. The target type of a lambda expression must be a functional interface, and lambda expressions can only create objects for functional interfaces, because lambda expressions can only implement one abstract method.

As mentioned earlier, when using a lambda expression, if there is only one line of code in the code body, curly braces can be omitted, and if there is a return value, the return keyword can also be omitted. In addition, when there is only one code in a lambda expression, other methods or constructors can be referenced and called automatically, parameter passing can be omitted, and the code is more concise, The syntax of the reference method requires the:: symbol. Lambda expressions provide four ways to reference methods and constructors:

Method class of reference object:: instance method
Reference class method class:: class method
Methods that reference specific objects: instance methods
Constructor class of reference class:: new
for instance:

public class HelloWorld
{   
    public static void main(String[] args)
    {   
        ArrayList<String> list = new ArrayList<>();
        list.add("Objective-C");
        list.add("Swift");
        list.add("Python");
        list.add("Golang");
        list.add("Java");
        //list.sort((s1, s2)->s1.compareTo(s2));
        list.sort(String::compareTo);
        list.forEach(System.out::println);
    }
}

Comparing the above two lines of code, the first sort function passes in a lambda expression to implement the compare function of the Comparator interface. Since the implementation has only one code, curly braces and the return keyword can be omitted. The second sort method directly refers to the instance method of the object. The syntax rule is class:: instance method. The system will automatically take the first parameter of all parameters of the method implemented by the functional interface as the caller, and the next parameters will be passed into the referenced method in turn, that is, S1 For the method call of CompareTo (S2), it is obvious that the second sort function call is more concise and clear.

The last line of code is list forEach(System.out::println); The class method is referenced. The instance method foreach of the collection class receives a Consumer interface object, which is a functional interface with only one abstract method void accept (T);, Therefore, you can use lambda expression to call. Here, you can refer to system The class method println of out refers to the syntax class:: class method. The system will automatically pass all parameters in the implemented functional interface method into this class method and call it automatically.

Another example:

@FunctionalInterface
interface Index
{
    int index(String subString);
}

@FunctionalInterface
interface Generator
{
    String generate();
}

public class HelloWorld
{   
    static int getIndex(Index t, String subString)
    {
        return t.index(subString);
    }
    
    static String generateString(Generator g)
    {
        return g.generate();
    }
    
    public static void main(String[] args)
    {   
        String str = "Hello World";
        System.out.println(getIndex(str::indexOf, "e"));
        System.out.println(generateString(String::new).length());
    }
}

This example doesn't seem to have any practical significance. It's just to demonstrate the instance method of referring to a specific object and the constructor of referring to a class. The interfaces Index and Generator are both functional interfaces, so you can use lambda expressions. For the getIndex method, you need to pass in an object and a substring that implement the Index interface. When calling, you first define a string Hello World, and then reference the instance method indexOf of this object. At this time, the system will automatically take this specific object as the caller, and then pass all parameters into this method.
The method of referring to the constructor is also very simple, class:: new, which will not be repeated.

summary
This article mainly explains the anonymous inner class, lambda expression, functional interface, method reference and constructor reference of lambda expression. Through several examples, we can find that the lambda expression provided by Java 8 is so powerful. The next article will explain the new Stream API in Java 8. The Stream API supports parallelism, improves the traditional programming method, and can write more concise and clear high-performance code. Interested readers can read the Java Stream API.

Author: wwdotpng
Link: https://www.jianshu.com/p/c832d2c6dd81
Source: Jianshu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

Keywords: Java Lambda

Added by Clandestinex337 on Thu, 10 Feb 2022 16:19:49 +0200