A preliminary study on Java anonymous inner classes and lambda expressions

Article directory

  • Preface
  • 1. Anonymous inner classes
    • 1.Features
    • 2. Grammar (examples)
  • 2. Lambda expression
    • 1.Features
    • 2. Grammar (examples)
  • 3. Java event processing mode
    • 1.Writing 1
    • 2.Writing 2
    • 3.Writing 3
    • 4.Writing 4

Foreword

When writing Java event processing recently, I basically used the traditional Java event processing mechanism at the beginning, new the implemented interface object, passed it to the listener, and then used anonymous inner classes and lambda expressions to write I wrote them all (anonymous inner classes and lambda expressions are relatively common in the source code of many frameworks), and then I found that I only knew how to write this anonymous inner class and lambda expression, but I didn’t know why. I was also very curious about three What kind of optimizations did the compiler probably do in this way of writing, so I took a look at it and took notes.

1. Anonymous inner class

There is a need to create objects that will not be used again, in this case anonymous inner classes are suitable. Such classes have no names and are declared and created in the same statement. Since it is an anonymous class, you cannot instantiate and use this class elsewhere. Anonymous inner classes can also be used to implement interfaces. The premise is that it must be a class or interface, and when there is only one abstract method in the class or interface, going from anonymous inner class to lambda is equivalent to helping us simplify the code more and more, so that when we simplify the code, the compiler can match your Implementation)

1.Features

Anonymous object: an object without a name. new ClassName().run();
Non-anonymous object: An object with a name. ClassName c=new ClassName(); c.run();

2. Grammar (examples)

Example 1: Creating a parent class and using anonymous inner classes is equivalent to creating a sub-interface that implements the parent interface.

public abstract class Animal {
    public abstract void eat();
}

/**
 * Prerequisite
 * Must be a class or interface
 * When there is only one abstract method in a class or interface
 */
public class Demo01 {
    public static void main(String[] args) {
        //This anonymous inner class is equivalent to: a subclass object of the Animal parent class
        new Animal(){
            @Override
            public void eat() {
                System.out.println(" Repair Dog");
            }
        };
        //Call method 1
        new Animal(){
            @Override
            public void eat() {
                System.out.println(" Repair Dog");
            }
        }.eat();
        //Call method two
        String name="Chinese Pastoral Dog";
//Access local variables through anonymous inner classes. Before the JDK8 version, you needed to use the final keyword. Now it can be automatically defaulted.
        Animal a=new Animal() {
            @Override
            public void eat() {
                System.out.println(name + " Repair Dog");
            }
        };
        a.eat();
    }
}

Example 2: Create two parent interfaces (one with parameters in the method, one without parameters), and use anonymous inner classes and lambdas to pass function parameters

public interface Inter {
    public void method();
}
public interface Inter2 {
     int add(int x,int y);
}
public class Demo02 {
    public static void main(String[] args) {
        //If you want to call the function method, the conventional method must first create an interface that implements Inter (new comes out) and pass it in
        //But using anonymous inner classes you can do this directly
        function(new Inter() {
            @Override
            public void method() {
                System.out.println("Anonymous inner class overridden method");
            }
        });
        //Use Lambda expressions, which is more concise and directly write the logic implementation. The specific matching compiler will help us implement it.
        function(()->{
            System.out.println("Rewritten method of Lambda expression");
        });
        //Anonymous inner class with parameters
        function2(new Inter2() {
            @Override
            public int add(int x, int y) {
                return x + y;
            }
        });
        //Lambda expression with parameters
        function2((int x,int y)->{
            return x + y;
        });
    }
    public static void function(Inter i){
        i.method();
    }
    public static void function2(Inter2 i){
        int num=i.add(1,2);
        System.out.println("Lambda expression with parameters:" + num);
    }
}

2. lambda expression

Lambda expression is an anonymous function, which is the most important new feature driving the release of Java 8.

Lambda allows functions to be passed as arguments to a method (functions are passed into methods as arguments).

Using lambda expressions can make the code more concise and compact.

1.Features

(formal parameters)->{code block}

Lambda expressions consist of three elements: brackets, arrows, and code blocks.

Code analysis of Lambda expressions
● (): There is no content in it. It can be regarded as the method formal parameter is empty.
● ->: Use arrows to point to things to be done later
● {}: Contains a piece of code, which we call a code block, which can be regarded as the content of the method body.

2. Grammar (examples)

Use anonymous inner classes and Lambda expressions to start a thread respectively. Compared with anonymous inner classes, we will find that Lambda expressions are more concise.

public class RunableDemo01 {<!-- -->
    public static void main(String[] args) {<!-- -->
        //Start a thread using an anonymous inner class
        new Thread(new Runnable() {<!-- -->
            @Override
            public void run() {<!-- -->
                System.out.println(Thread.currentThread().getName() + "Thread started");
            }
        }).start();
        //Lambda expression method to start a thread
        new Thread(()-> System.out.println(Thread.currentThread().getName() + "Thread started")).start();
    }
}

3. java event processing mode

The processing mode flow is shown in the figure (the wps drawing is slightly rough, but the overall idea is similar)

Four common writing methods for handling events (the first writing method posts all the demo code, and the remaining three methods post the changed parts based on the first writing method)

1.Writing 1

Create a monitor listener that implements the ActionListener interface and pass it in text.addActionListener(listener).

public class Demo03{
    public static void main(String[] args) {
        WindowActionEvent windowActionEvent=new WindowActionEvent();
        windowActionEvent.setBounds(100,100,310,260);
        windowActionEvent.setTitle("Handling ActionEvent events");
    }
}
class WindowActionEvent extends JFrame {
        JTextField text;
        ReaderListen listener;
        public WindowActionEvent(){
            init();
            setVisible(true);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }
        void init(){
            setLayout(new FlowLayout());
            text=new JTextField(10);
            //Traditional thinking
            listener=new ReaderListen();
            text.addActionListener(listener);
            
            add(text);
        }
}
class ReaderListen implements ActionListener{
    @Override
    public void actionPerformed(ActionEvent e) {
        String str=e.getActionCommand();
        System.out.println(str.length());
    }
}

2.Writing 2

There is no need to create the above class ReaderListen, just use the anonymous inner class to pass parameters directly.

//Use anonymous inner classes to simplify the code
text.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Use anonymous inner classes");
    }
});

3. Writing method 3

You don’t even need to know the class name, just write the implementation logic directly. However, please note that the premise is that there is only one abstract method in the class or interface.

```bash
//Use Lambda expressions for a more brief introduction
text.addActionListener(d->{
    System.out.println("Use Lambda expression");
});

4.Writing 4

In actual development, if the logic of event processing is relatively complex, taking into account the maintenance of code quality and low coupling, the logic implementation in the third expression will be encapsulated into a function.

void init(){<!-- -->
    setLayout(new FlowLayout());
    text=new JTextField(10);
    //Traditional thinking
    //Use Lambda expression, d is the parameter
    text.addActionListener(d->{<!-- -->
        onClick(d);
    });

    add(text);
}
//Use custom function onClick to encapsulate logic
public void onClick(ActionEvent event){<!-- -->
    System.out.println("lambda but decoupled version");
}