匿名内部类 - javac如何使用lambda表达式?

时间:2017-09-25 18:29:49

标签: java lambda

在下面的工作代码中,

package com.ca.naive;

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.List;
import java.awt.Panel;
import java.awt.TextField;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class TodoGUI {
    public static void main(String[] args) {
        List list = new List();
        TextField itemField = new TextField();

        Button addButton = new Button("Add");
        Button removeButton = new Button("Remove");

        addButton.addActionListener(e -> list.add(itemField.getText()));
        removeButton.addActionListener( e -> list.remove(list.getSelectedIndex()));

        Panel buttons = new Panel(new GridLayout(1,0,3,3));
        buttons.add(addButton);
        buttons.add(removeButton);

        Panel bottomPanel = new Panel(new FlowLayout(FlowLayout.RIGHT));
        bottomPanel.add(buttons);

        Panel centerPanel = new Panel(new BorderLayout());
        centerPanel.add(BorderLayout.NORTH, itemField);
        centerPanel.add(BorderLayout.SOUTH, buttons);

        Frame frame = new Frame();
        frame.setLayout( new BorderLayout() );
        frame.add(BorderLayout.WEST, list);
        frame.add(BorderLayout.CENTER, centerPanel);
        frame.pack();
        frame.addWindowListener( new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        frame.setVisible(true);
    }
}

javac是否用

替换addButton.addActionListener(e -> list.add(itemField.getText()));语法
addButton.addActionListener(new java.awt.Event.ActionListener() {
    public void actionPerformed(java.awt.Event.ActionEvent e) {
        list.add(itemField.getText());
    }
});

2 个答案:

答案 0 :(得分:2)

  

javac是否用

替换addButton.addActionListener(e -> list.add(itemField.getText()));语法
addButton.addActionListener(new java.awt.Event.ActionListener() {
    public void actionPerformed(java.awt.Event.ActionEvent e) {
        list.add(itemField.getText());
    }
  });
     

不,这是编译器的功能。在您的示例中,将生成并实例化ActionListener实现,但这不会在编译时发生;它发生在运行时。编译器做了两件事。首先,它将lambda的主体移动到一个看起来像这样的隐藏方法:

void lambda$1(java.util.List list, java.awt.TextField itemField) {
    list.add(itemField.getText());
}

其次,在声明lambda的位置,它会发出对 bootstrap 方法的调用。引导方法是一种特殊的工厂方法,它知道如何生成功能接口的实现。需要给出一些基本信息,最值得注意的是:功能接口的类型(已知为ActionListener);任何捕获变量的类型(在您的情况下,listitemField);以及哪个方法包含实现的逻辑(生成的lambda$1方法)。

当引导程序调用在运行时被命中时,它将生成ActionListener实现。下次结束此代码路径时,您不必调用bootsrap方法。相反,替换了引导程序调用,以便最终得到相当于:

的内容
addButton.addActionListener(TodoGUI$Lambda$1.getInstance(list, itemField));

TodoGUI$Lambda$1是一个看起来像这样的类:

static class TodoGUI$Lambda$1 implements java.awt.Event.ActionListener {
    private final java.util.List list;
    private final java.awt.TextField itemField;

    TodoGUI$Lambda$1(java.util.List list, java.awt.TextField itemField) {
        this.list = list;
        this.itemField = itemField;
    }

    @Override
    public void actionPerformed(java.awt.Event.ActionEvent e) {
        TodoGUI.lambda$1(list, itemField);
    }

    static java.awt.Event.ActionListener getInstance(
        java.util.List list,
        java.awt.TextField itemField) {

        return new TodoGUI$Lambda$1(list, itemField);
    }
}

现在,考虑到所有这些,编译器需要您导入ActionListener类型。那种类型根本不需要在词法范围内。编译器将查看并在addActionListener实例上调用名为java.awt.Button的方法。它将看到您传递的是一个参数,这是一个lambda表达式。在这种情况下,没有重载,因此它知道addActionListener希望您传递ActionListener。它看到ActionListener是一个单方法接口,这意味着它可以绑定到lambda。它尝试推断您的参数类型和返回类型,使它们与ActionListener的预期内容兼容:单个ActionEvent参数和void返回类型。您的lambda是兼容的,因此绑定了调用,并执行上述步骤。

答案 1 :(得分:0)

另一个"非lambda"您描述的效果示例是

之类的调用
System.out.println("hello");

您不需要导入java.io.PrintStream,以便使用println作为此类实例附带的out方法。

顺便说一句:你可以在不使用任何import语句的情况下编写一个类。在这种情况下,如果要使用它,则必须使用包括所有包的完整类名:

java.io.PrintStream outStream = System.out;
outStream.println("Hello");

每次使用类时,import只会为您节省重复的包规范。