2017-09-25 101 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只是保存你的包每次使用一個類時反覆規範。