2017-09-25 65 views
-1

在下面的工作代码,匿名内部类 - javac如何与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()); 
    } 
}); 

+1

_ [an'import'语句] ...允许您使用单个标识符(例如'List','min')来引用类型或静态成员,而不是完全限定的名称(例如'java .util.List','Math.min')_你没有使用类型名称,所以你不需要'import'语句。 –

+0

@SotiriosDelimanolis你的意思是,'javac'通过检测传递给'addButton.addActionListener()'的对象的类型在内部解析类型名称'ActionListener'?在提出此查询之前,您的评论的第一部分是已知的 – overexchange

+1

是的,它知道您尝试调用的方法需要一个'ActionListener'。据推测,这是唯一适用的方法。但是对于_resolve name_,则不需要解析名称,因为您没有使用名称。 'import'语句只是简化了源代码。他们什么都不做。 –

回答

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实现被生成和实例化,但是这不会发生在编译时;它发生在运行时。编译器有两件事。首先,移动你的拉姆达的身体变成一个隐藏的方法,看起来是这样的:

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

其次,在那里你拉姆达声明来看,其发射到一个引导方法的调用。引导方法是一种特殊的工厂方法,它知道如何生成功能接口的实现。它需要提供一些基本信息,最显着的是:功能接口的类型(已知为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类型。这种类型根本不需要在词汇范围内。编译器会查看并看到您在调用java.awt.Button实例上调用addActionListener的方法。它会看到你正在传递一个参数,这是一个lambda表达式。在这种情况下,没有超载,所以它知道addActionListener希望你通过ActionListener。它看到ActionListener是一个单一方法接口,这意味着它可以绑定到lambda。它会尝试推断出您的参数类型和返回类型,以便它们与ActionListener:单个ActionEvent参数和void返回类型的预期值兼容。你的lambda是兼容的,所以这个调用是绑定的,并执行上述步骤。

0

你描述的效果的另一种“非拉姆达”的例子是像

System.out.println("hello"); 

呼叫你不需要为了使用随out是实例的println方法导入java.io.PrintStream这个班。

顺便说一句:你可以在没有使用任何import语句的情况下编程一个类。在这种情况下,你总是要使用完整的类名,包括所有的包,如果你想使用它:

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

import只是保存你的包每次使用一个类时反复规范。