Java8 A simple understanding of Lambda expressions and functional interfaces

  • 2020-11-30 08:22:45
  • OfStack

Java8 has been described as the biggest change in the history of Java. There are a number of important new features, the most important being the addition of Lambda expressions and StreamAPI. The two can also be used in combination at 1. First let's look at what an Lambda expression is.

Using Lambda expressions not only makes the code simpler, but also makes it readable and, most importantly, reduces the amount of code. However, to some extent, these features are already widely used in JVM languages such as Scala.

Not surprisingly, the Scala community is incredible, as much of the content in Java 8 looks as if it were taken from Scala. In a way, the syntax of Java 8 is more detailed but less clear than that of Scala, but that doesn't say much, and if it could, it might build Lambda expressions just like Scala.

On the one hand, if Java continues to evolve around Lambda and implement features that both Scala have already implemented, then Scala may not be needed. On the other hand, if it only provides 1 core function, such as helping anonymous inner classes, then Scala and other languages will continue to thrive and potentially surpass Java. This is for the best. Competition leads to progress, and other languages continue to evolve and grow without worrying about obsolescence.

Lambda expression, wikipedia's definition is an operator used to represent anonymous functions and closures, but it still feels abstract, so let's take a look at an example


public class SwingTest {
  public static void main(String[] args) {
    JFrame jFrame = new JFrame("My JFrame");
    JButton jButton = new JButton("My JButton");

    jButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {        
        System.out.println("Button Pressed!");
      } 
    }); 
    
    jFrame.add(jButton); jFrame.pack(); 
    jFrame.setVisible(true); 
    jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
  }
}

This is 1 piece of Swing programming code. Bind Button to 1 listening event. When clicking Button, it will print "ButtonPressed!" in the console. The content. An instance of an anonymous inner class is created to bind to the listener, which is the usual way of organizing code. But a closer look shows that what we really care about is the argument e of type ActionEvent and the statement to the console System.out.println ("ButtonPressed! ); .

If you replace the code in the previous program that created the interface instance as an anonymous inner class with an Lambda expression, the code looks like this


public class SwingTest {

public static void main(String[] args) {
  JFrame jFrame = new JFrame("My JFrame");
  JButton jButton = new JButton("My JButton");

  jButton.addActionListener(e -> System.out.println("Button Pressed!"));

  jFrame.add(jButton);
  jFrame.pack();
  jFrame.setVisible(true);
  jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

Focus on the middle part of the code, instead of the original 6 lines of code, now 1 line can be implemented. This is a simple form of the Lambda expression.
You can see that the syntax for the Lambda expression is


(param1,param2,param3) -> {

//todo
}

Here, the type of the parameter can be inferred from the context, but not all types can be inferred, at this point we need to display the declared parameter type, when there is only one parameter the braces can be omitted. When the todo section has only 1 line of code, the outer braces can be omitted. As in our example above

So in addition to the code being clean, what else has the Lambda expression changed?

Let's recall that in Java, we were unable to pass a function as a parameter to a method, or declare a method whose return value was a function. Before Java8, the answer was yes.

So, in the above example, we can actually pass a piece of code logic to the listener as a parameter, telling the listener that you can do so when an event is triggered, instead of having to use an anonymous inner class as a parameter. This is another new feature of Java8: functional programming.

There are many languages that support functional programming. In JavaScript, it is very common to pass a function as an argument or return a value of 1 function. JavaScript is a very common functional language.

Lambda added the missing features of functional programming to Java, allowing us to treat functions as citizens of class 1.

In functional programming languages, the type of Lambda expression is a function. In Java, Lambda expressions are objects that must be attached to a particular object type of class 1, the functional interface (FunctionalInterface).

Let's take a look at the definition of a functional interface:

If an interface has one and only one abstract method (not included in the Object class), the interface can be considered a functional interface.


@FunctionalInterface
public interface Runnable {
  /**
   * When an object implementing interface <code>Runnable</code> is used
   * to create a thread, starting the thread causes the object's
   * <code>run</code> method to be called in that separately executing
   * thread.
   * <p>
   * The general contract of the method <code>run</code> is that it may
   * take any action whatsoever.
   *
   * @see   java.lang.Thread#run()
   */
  public abstract void run();
}

Consider the declaration of the Runnable interface. After Java8, the Runnable interface has an additional FunctionalInterface annotation, indicating that the interface is a functional interface. But if we do not add the FunctionalInterface annotation, the compiler will also treat the interface as a functional interface if there is only one abstract method in the interface.


@FunctionalInterface
public interface MyInterface {
  void test();
  String toString();
}

MyInterface This is also a functional interface, because toString() is a method in the Object class that is only copied here and does not increase the number of abstract methods in the interface.

(As an additional note, in Java8, methods in the interface can have not only abstract methods, but also implemented methods, called default methods (defaultmethod), which will be described later in this section.)

Since in Java, Lambda expressions are objects. So what is the type of this object? Let's go back to the SwingTest program, where an instance of the ActionListener interface is created as an anonymous inner class


jButton.addActionListener(new ActionListener() {
  @Override
  public void actionPerformed(ActionEvent e) {        
    System.out.println("Button Pressed!");
  } 
}); 

Improved using Lambda expressions


jButton.addActionListener(e -> System.out.println("Button Pressed!"));

That is, we created an instance of the ActionListener interface using the Lambda expression. Let's look at the definition of the ActionListener interface


public interface ActionListener extends EventListener {
  /**
   * Invoked when an action occurs.
   */
  public void actionPerformed(ActionEvent e);
}

There is only one abstract method, which, though not annotated with the FunctionalInterface annotations, conforms to the definition of a functional interface, which the compiler considers to be a functional interface.
So, using the Lambda expression, you can create instances of functional interfaces. That is, the Lambda expression returns a functional interface type.

In fact, functional interface instances can be created in three ways (see the FunctionalInterface annotations) :

1. Lambda expression
2. Method references
3. Constructor reference

conclusion

That's it for Java8, a simple look at Lambda expressions and functional interfaces. Interested friends can continue to refer to other relevant topics in this site, if there is any deficiency, welcome to leave a message!


Related articles: