What is a Lambda expression

We know that an interface can’t be instantiated directly, so you have to new its implementation class, but if the current interface has only one abstract method and you don’t want to create another class, before JDK8, you had to use anonymous inner classes. Something like this:

   new Thread(new Runnable() {
            @Override
            public void run(a) {
                System.out.println("hello world");
            }
        }).start();
Copy the code

Today’s ides are so powerful that you just write new Runnable() to complete your code, but you still have to move the cursor inside the run method to write the corresponding code. When I learned about anonymous inner classes, I wished my code would be simpler and more compact. (In fact, The official guide to The Java™ Tutorials had lambdas under The chapter on anonymous classes. The chapter on Lambda expressions was written at: Docs.oracle.com/javase/tuto…). . After JDK8, the above code can be simplified as follows:

 new Thread(()-> System.out.println("hello world")).start();
Copy the code

Isn’t it pretty neat? ()-> system.out.println (” Hello world”) is called a Lambda expression. Lambda is the name of the symbol λ. Wikipedia describes Lambda expressions like this:

A function (or a subroutine) defined, and possibly called, without being bound to an identifier.

A function or subroutine that may be called but is not bound to an identifier.

Let’s put it in Java, because Java is object-oriented, and functions are put in a class. Methods are called in one of two forms:

  • Class name. Method name
  • Class instance. Method name.

So in Java, we can just call the class name + method name an identifier, because we are locating a method by the class name (strictly speaking, the full class name)+ method name. Println (“hello world”) -> system.out.println (“hello world”) -> system.out.println (“hello world”);

Baidu Baike describes Lambda expressions like this:

Lambda expression is an anonymous function, named after the Lambda calculus in mathematics and directly corresponds to its Lambda abstraction, which is an anonymous function without a function name. — Baidu Encyclopedia

We know that calculus in mathematics requires operators, that calling a function requires parameters, that calling a function requires a method body.

-> is the operator, and -> is the body of the method. Expressions of the form ()->{} are called Lambda expressions in Java. Because the run method in the Runnable interface has no arguments, nothing is written inside ().

Lambda expressions, by the way, were not first introduced in Java. .NET Framework 3.5 was introduced on November 19, 2007. Jdk8 was released on March 18, 2014. All major programming languages should have this concept.

So let’s think about how we can simplify this, if we have two methods:

  • add(int i)
  • add(Long i)

We call this method overloading, the same method name, different parameter types. When we call, the compiler, or JVM, can call the corresponding method based on the parameters we’re given. New Thread(()-> system.out.println (“hello world”))) to infer which constructor of Thread it is. So it looks like you wrote this:

 new Thread(()-> System.out.println("hello world")).start();
Copy the code

In fact, the compiler will do it for you:

   new Thread(new Runnable() {
            @Override
            public void run(a) {
                System.out.println("hello world");
            }
        }).start();
Copy the code

That’s why some people say Lambda underperforms in some cases.

Applicable scenario

If you open up the Runnable interface, there’s an annotation on the Runnable interface: @functionalInterface. FunctionalInterface stands for FunctionalInterface. What is a FunctionalInterface? An interface that has only one abstract method in it is called a functional interface. We know that before JDK8, we can only put abstract methods in the interface. After JDK8, we can put non-abstract methods. Methods include the default keyword, which we call the default implementation. The form is as follows:

@FunctionalInterface
public interface MyLambdaInterface {
    void sayHello(String str);
    default void sayWorld(String str){ System.out.println(str); }}Copy the code

An interface with @functionalInterface can have only one abstract method, allowing methods with the same name as Object, but requiring the same parameter type. Otherwise it will fail the compilation. As follows:

@FunctionalInterface
public interface MyLambdaInterface {
    void sayHello(String str);
    int hashCode(a);
    boolean equals(Object obj);
    default void sayWorld(String str){ System.out.println(str); }}Copy the code

Lambda expressions come in handy if I have a functional interface, but I don’t want to create a display implementation Class for that Class (the display implementation Class is a new Class file), and I want to use this method.

How to use and how many forms can Lambda expressions take

We will first introduce the form of Lambda expressions and then explain how to use them.

  • The method argument Lambda is formally more like a function,(the arguments required by the function)->{method implementation}

New Thread(()-> system.out.println (” Hello world”)).start(); This is an example. Although in principle a thread should not be created directly, it should be created through a thread pool. New Thread(()-> system.out.println (” Hello world”)); new Thread(()-> system.out.println (” Hello world”))

  • For example, MyLambdaInterface, a method reference can be written like this:
MyLambdaInterface myLambdaInterface = (str)-> System.out.println(str);
Copy the code

Formally, myLambdaInterface points to a function. (str)-> System.out.println(str); Is an implementation of the method sayHello.

  • The syntax for pointing to an existing method is:
  • Reference to a static method:
public class Person {
    LocalDate birthday;
    public static int compareByAge(Person a, Person b) {
        returna.birthday.compareTo(b.birthday); }}Copy the code

Method references are written as Person::compareByAge

However, if this statement appears directly in code, it will cause an error, so we need to find a functional interface to point to it. The method return types and receive parameters in a functional interface should be consistent with compareByAge, as shown below.

public interface CompareTo<T> {
    int compare(T t1,T t2);
}
Copy the code
  CompareTo<Person> p = Person::compareByAge;
Copy the code
  • Reference to an instance method of an arbitrary object of a particular type
  String[] stringArray = {"Barbara"."James"."Mary"."John"."Patricia"."Robert"."Michael"."Linda"};
        Arrays.sort(stringArray, String::compareToIgnoreCase);
Copy the code
  • Reference to an instance method of a particular object first you need to new an object, The form is either the functional interface = object :: method or passing the object :: method as a method parameter.
  • Class name :: new. The usage is the same as the previous three, and can be used as an implementation of a functional interface or as a method parameter.

Lambda expressions are intrinsically inner classes, so just like inner classes, to access a local variable, the local variable must be of final type. Now let’s summarize Lambda expressions: (parameters required by a functional interface)-> method body; If the method body is a single line, you don’t need to write {for example:

   new Thread(()-> System.out.println()).start();
Copy the code

If you have more than one line, you say {

We can think of each Lambda expression as an anonymous implementation of a functional interface.

Functional interfaces provided by the JDK

The JDK also has some built-in functional interfaces, most of which are under java.util. Function. Understanding these functional interfaces is key to learning about the Stream API. Here we choose a few more classic explanation.

  • The abstract methods used in Predicate assertions are: Boolean test(T T);
  • Void accept(T T);
  • Function

    R apply(T T); ‘
    ,>
  • Supplier, abstract method: T get();

boolean test(T t); This method takes a parameter and returns a Boolean value.

We can write this:

 Predicate<String> predicate = (str)-> str.contains("s");
Copy the code

You can also write:

  List<String> list = new ArrayList<>();
  Predicate<String> predicate = list::add;
  predicate.test("add");// Equivalent to calling list.add("add")
  predicate.test("bb");
 
Copy the code

Consumer, Function, and Supplier are used similarly, so I won’t go into too much detail here.

Programmatically into the Stream API

First, Stream is an interface located under java.util. Stream. Suppose our collection generic is a student class, as follows.

 List<Student> student = new ArrayList<>();
Copy the code

So if I want to count the total number of students over the age of 18, this is what we usually do when we don’t stream.

   int count = 0;
    List<Student> studentList = new ArrayList<>();
    for (Student student : studentList) {
        if (student.getAge() > 18){ count++; }}Copy the code

Student can be mapped to a database, it’s easy to do this in a database, but can the code perform count, WHERE operations like SQL. Once we have a stream, we can write it like this:

 studentStream.filter( (student) -> student.getAge() > 18).count();
Copy the code

Did it become much simpler! Can I group by in count? That’s ok. Find the number of boys and girls grouped by sex.

 Map<Boolean, Long> result = studentStream.collect(Collectors.groupingBy(Student::isSex, counting()));
Copy the code

A closer look at his implementation is not the subject of this article, which is just a primer, but a detailed discussion is another. Incidentally, there are two types of streams, serial and parallel streams, which will be the focus of our next article.

To summarize

  • Lambda expressions make code more compact, allowing methods to be passed as arguments.
  • By moving further from the Lambda method to Stream, we can port some database operations, such as WHERE and group by, into the code with greater simplicity.

Reference: The Java™ Tutorials