preface

In normal development, we sometimes create a thread to do something by implementing the Runnable or Callable interface, but this code is one-off, and it would be redundant to write a separate implementation class for Runnable or Callable. Some students may think that we can use the way of anonymous inner class to solve this problem, but the way of anonymous inner class code is too much and bloated, although we can use development tools to automatically generate these codes, but give people the feeling that the code is not simple and beautiful!

To address this issue, Oracle brought a concise Lambda expression in Java8, released in March 2014!

1) Lambda expressions

  • Java8 is a major release of the Java language development. Java8 was released by oracle in March 2014 and can be considered the most revolutionary release since Java5. Java8 brings a host of new features to the Java language, compilers, class libraries, development tools, and JVMS. Lambda expressions are a big new feature of Java8!

  • Why Lambda expressions?

    • Lambda is an anonymous function, and we can think of a Lambda expression as a piece of code that can be passed (passing code like data). It allows you to write cleaner, more flexible code. As a more compact code style, the language expression ability of Java has been improved.
  • Let’s start with two examples to experience the brevity of Lambda expressions:

    • Patients with a

      public static void main(String[] args) {
          // Anonymous inner class
          Runnable r1 = new Runnable() {
              @Override
              public void run(a) {
                  System.out.println("Hello World!"); }}/ / Lambda expressions
          Runnable r2 = () -> System.out.println("Hello Lambda!");
      }
      Copy the code
    • Example 2

      public static void main(String[] args) {
          // Use an anonymous inner class as an argument
          TreeSet<String> ts1 = new TreeSet<>(new Comparator<String>() {
              @Override
              public int compare(String o1, String o2) {
                  returnInteger.compare(o1.length(), o2.length()); }});//Lambda expressions are passed as arguments
          TreeSet<String> ts2 = new TreeSet<>(
          	(o1, o2) -> Integer.compare(o1.length(), o2.length())
          );
      }
      Copy the code

    As you can see from the above two examples, the way Lambda expressions work is incredibly simple compared to the previous bloat code that implemented classes anonymously.

  • Let’s learn the syntax of Lambda expressions.

    • Lambda expressions: a new syntactic element and operator introduced in the Java8 language. The operator is “->”, which is called the Lambda operator or arrow operator. It breaks Lambda into two parts:

      • Left: Specifies the list of arguments required by the Lambda expression
      • On the right: specifies the Lambda body, which is the implementation logic of the abstract method and what Lambda expressions are supposed to do.
    • Syntax format 1: No parameter, no return value.

      Runnable r1 = () -> {System.out.println("Hello Lambda!"); };Copy the code
    • Lambda takes one argument, but returns no value.

      Consumer<String> con = (String str) -> {System.out.println(str); };Copy the code
    • Syntax format 3: Data types can be omitted because they can be inferred by the compiler, called type inference.

      Consumer<String> con = (str) -> {System.out.println(str); };Copy the code
    • Lambda if only one argument is required, the parentheses of the argument can be omitted.

      Consumer<String> con = str -> {System.out.println(str); };Copy the code
    • Lambda takes two or more arguments, multiple execution statements, and can have a return value.

      Comparator<Integer> com = (x, y) -> {
          System.out.println("Implement functional interface methods!");
          return Integer.compare(x, y);
      };
      Copy the code
    • Syntax Format 6: When the Lambda body has only one statement, the return and braces, if present, can be omitted.

      Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
      Copy the code
  • What is type inference?

    The types of arguments in Lambda expressions above are inferred by the compiler. Programs can compile without specifying a type in a Lambda expression, because Javac behind the scenes deduces the type of the argument based on the context of the program. The type of a Lambda expression is context-dependent and inferred by the compiler. This is called “type inference”.

  • After learning about Lambda expressions, I wonder if you have noticed one problem. That is, the interface implemented by Lambda examples above has only one abstract method! What is the reason for this? Let’s move on to functional interfaces!

2) Functional interface

  • What is a Functional interface

    • An interface that contains only one abstract method is called a functional interface.
    • You can use Lambda expressions to create objects for this interface. If a Lambda expression throws a checked exception (that is, a non-runtime exception), the exception needs to be declared on the abstract method of the target interface.
    • We can use the ** @funcationalInterface ** annotation on an interface to check if it is a functional interface. Javadoc also contains a declaration that the interface is a functional interface.
    • Java8’s rich functional interfaces are defined under the java.util.function package.
  • How to understand functional interfaces

    • Java has been an “everything is object” proposition since its birth, and object-oriented (OOP) programming in Java is everything. But with the rise of languages such as Python and Scala and the challenges of new technologies, Java has had to adapt to support a wider range of technical requirements, that is, Java can support BOTH OOP and OOF (functional oriented programming).
    • In functional programming languages, functions are treated as first-class citizens. In the programming reason for making functions first-class citizens, Lambda expressions are of type functions. But in Java8, it’s different. In Java8, Lambda expressions are objects, not functions, and they must be attached to a special class of object types called functional interfaces.
    • Simply put, in Java8, a Lambda expression is an instance of a functional interface. This is the relationship between Lambda expressions and functional interfaces. That is, as long as an object is an instance of a functional interface, that object can be represented as a Lambda expression.
    • So anything previously represented as an anonymous implementation class can now be written as a Lambda expression.
  • Take a chestnut

    • Our common Runnable interface is a functional interface

  • Of course, we can also define a functional interface ourselves:

The @functionalinterface annotation is just a validation. If you didn’t write the annotation, the interface would still be a FunctionalInterface. The point of a FunctionalInterface is that it only contains an abstract method, not the @functionalinterface annotation. The @FunctionalInterface annotation is mainly used for validation and declaration in Javadoc.

  • If you want to pass a Lambda expression as a parameter, the argument type that receives the Lambda expression must be the type of the functional interface compatible with the Lambda expression.

  • Java defines Java8’s rich functional interfaces under the java.util.function package

    • Four core functional interfaces

      Functional interface The parameter types The return type use
      Consumer<T> Consumer interface T void Apply an operation to an object of type T, including methods:

      void accept(T t)
      Supplier<T> Supplier interface There is no T Return an object of type T containing methods:

      T get()
      Function<T, R T R Applies the operation to an object of type T and returns the result. The result is an object of type R. R apply(T T)
      Predicate<T> Predicate interface T boolean Determines whether an object of type T satisfies a constraint and returns Boolean. Boolean test(T T)
    • Other interfaces

      Functional interface The parameter types The return type use
      BiFunction<T, U, R> T, U R Apply an operation to a parameter of type T, U, and return a result of type R. R apply(T T, U U);
      UnaryOperator<T> (Function subinterface) T T Performs unary operation on an object of type T and returns a result of type T. T apply(T T);
      BinaryOperator<T> (BiFunction subinterface) T, T T Performs a binary operation on an object of type T and returns a result of type T. T apply(T t1, T t2);
      BiConsumer<T, U> T, U void Apply the operation to parameters of type T and U. The inclusion method is:

      void accept(T t, U u);
      BiPredicate<T, U> T, U boolean Boolean test(T T, U U);
      ToIntFunction<T>

      ToLongFunction<T>

      ToDoubleFunction<T>
      T int

      long

      double
      A function that evaluates int, long, and double, respectively
      IntFunction<R>

      LongFunction<R>

      DoubleFunction<R>
      int

      long

      double
      R Functions that take int, long, and double, respectively
  • The functional interface is at the heart of Lambda expressions! Lambda expressions can only work on functional interfaces!

  • So Lambda expressions are the simplest way to write them? Not so! There’s even an easier way to write it! Let’s take a look at method references!

3) Method references and constructor references

  • Method References

    • When passing an operation to a Lambda body that already has a method implemented, use a method reference!

    • Method references can be thought of as deeper expressions of Lambda expressions. In other words, a method reference is a Lambda expression, an instance of a functional interface that points to a method by its name and can be thought of as a syntactic sugar for a Lambda expression.

    • Requirement: The argument list and return value type of the abstract method implementing the interface must be the same as the argument list and return value type of the method referenced by the method!

    • Format: Use the :: operator to separate a class (or object) from a method name.

    • There are three main uses:

      • Object :: Instance method name
      • Class :: Static method name
      • Class :: Instance method name
    • Here’s an example:

      // Example 1 object :: instance method name
      / / Lambda method
      Consumer<String> con = (x) -> System.out.println(x);
      // Method reference
      Consumer<String> con2 = System.out :: println;
      
      // Static method name
      / / Lambda method
      Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
      // Method reference
      Comparator<Integer> com1 = Integer :: compare;
      
      // Example three classes :: instance method name
      / / Lambda method
      BiPredicate<String, String> bp = (x, y) -> x.equlas(y);
      // Method reference
      BiPredicate<String, String> bp1 = String :: equals;
      Copy the code
    • Note: When the first argument to a functional interface method is the caller that needs to reference the method, and the second argument is the argument (or no argument) that needs to reference the method: ClassName :: methodName

  • Constructor reference

    • Format: ClassName :: new

    • Combined with a functional interface, automatically compatible with methods in the functional interface.

    • Constructor references can be assigned to defined methods, requiring that the constructor argument list matches the argument list of the abstract method in the interface! And the return value of the method is the object of the constructor class.

    • For example:

      / / Lambda method
      Function<Integer, MyClass> fun = (n) -> new MyClass(n);
      
      // Constructor reference
      Function<Integer, MyClass> fun = MyClass :: new;
      Copy the code
  • An array reference

    • Format: type[] :: new

    • For example:

      / / Lambda method
      Function<Integer, Integer[]> fun = (n) -> Integer[n];
      
      // Array reference
      Function<Integer, Integer[]> fun = Integer[] :: new;
      Copy the code

4) IDEA automatically simplifies code

The following operations are only for students using IntelliJ IDEA development.

If you still don’t know how to use Lambda expressions and method references to simplify anonymous internal implementation classes for functional interfaces, don’t panic, we have the powerful development tool IntelliJ IDEA!

We can write the simplest anonymous implementation class for the Comparator interface:

Comparator<Integer> com = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        returnInteger.compare(o1, o2); }};Copy the code

If you copy the above code into IDEA, you can see that part of the code is gray, and if you place the mouse over the gray area, IDEA will intelligently prompt you: Anonymous New Comparator<Integer>() can be replaced with lambda (the Anonymous Comparator() implementation class can be replaced with lambda) and supports one-key substitution!

Click on it and the code above will look like this:

Comparator<Integer> com = (o1, o2) -> Integer.compare(o1, o2);
Copy the code

But you’ll notice that the code is still gray after you replace it with a Lambda expression, indicating that you can further replace it with a method reference! One-click replacement is still supported!

Repeat the above operation idea will help you replace with the easiest code writing method!

Comparator<Integer> com = Integer :: compare;
Copy the code

So even if you don’t know how to write the simplest version, use powerful development tools to replace the clunky implementation with the simplest!

Ps: THE author is using version 2019.3.4 of IDEA. I have not tested whether other versions of IDEA and other development tools have this function. If you know about it, please discuss it in the comments section.

conclusion

That’s it for Lambda expressions and functional interface and method references, a new feature of Java8.

I would like to give you a star (PS: Don’t be stingy!)