什麼是Lambda表達式?java
在如 Lisp、Python、Ruby 編程語言中,Lambda 是一個用於表示匿名函數或閉包的運算符編程
爲什麼須要lambda表達式?閉包
- 在 Java 中,咱們沒法將函數做爲參數傳遞給一個方法,也沒法聲明返回一個函數的方法。
- 在 JavaScript 中,函數參數是一個函數,返回值是另外一個函數的狀況是很是常見的;JavaScript 是一門很是典型的函數式語言。
看以下匿名內部類實例:編程語言
1 import javax.swing.*; 2 import java.awt.event.ActionEvent; 3 import java.awt.event.ActionListener; 4 5 public class SwingTest { 6 public static void main(String[] args) { 7 JFrame jFrame = new JFrame("My JFrame"); 8 JButton jButton = new JButton("My Button"); 9 // 匿名內部類 10 jButton.addActionListener(new ActionListener() { 11 @Override 12 public void actionPerformed(ActionEvent e) { 13 System.out.println("Button Pressed"); 14 } 15 }); 16 jFrame.add(jButton); 17 jFrame.pack(); 18 jFrame.setVisible(true); 19 jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 20 } 21 }
如上代碼很簡單,經過第 10 行給第 8 行的按鈕註冊了一個監聽器。而咱們知道,當事件觸發真正起做用的其實是執行第 12 行的 actionPerformed 方法,爲此咱們建立了 ActionListener 實例。ide
而 Lambda 表達式可讓咱們只專一於方法的實現,在註冊監聽器時咱們能夠忽略具體須要建立哪一個類型的實例。其實在 Idea 中自己就已經給咱們提示了,以下:函數式編程
即匿名內部類的寫法是能夠用 Lambda 表達式替換的,以下:函數
1 import javax.swing.*; 2 3 public class SwingTest { 4 public static void main(String[] args) { 5 JFrame jFrame = new JFrame("My JFrame"); 6 JButton jButton = new JButton("My Button"); 7 // Lambda 表達式 8 jButton.addActionListener(event -> System.out.println("jButton Pressed")); 9 jFrame.add(jButton); 10 jFrame.pack(); 11 jFrame.setVisible(true); 12 jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 13 } 14 }
在例 2 中經過第 8 行的 Lambda 表達式替換了例 1 中 10-15 行的匿名內部類寫法,不只代碼更簡潔,並且含義更直觀明瞭。ui
在 Java 8 中 Lambda 表達式最原始的寫法以下:this
(類型 prarm1,類型 param2,類型 param3, ...) -> { // 方法體 }
能夠將參數類型省略,編譯器可自動推斷:spa
(prarm1, param2, param3, ...) -> { // 方法體 }
若是隻有一個參數,還能夠省略小括號:
prarm1 -> { // 方法體 }
若是方法體只有一行代碼,那麼大括號也能夠省略:
prarm1 -> <方法實現>
用例 2 中的 Lambda 爲例演變過程以下:
// 原始寫法 jButton.addActionListener((ActionEvent event) -> { System.out.println("jButton Pressed"); }); // 省略參數類型 jButton.addActionListener((event) -> { System.out.println("jButton Pressed"); }); // 省略小括號 jButton.addActionListener(event -> { System.out.println("jButton Pressed"); }); // 省略大括號 jButton.addActionListener(event -> System.out.println("jButton Pressed"));
在 Java 8 中, Iterable 接口中新增了一個默認函數 forEach 讓咱們能夠更方便的遍歷它:
1 package java.lang; 2 3 import java.util.Iterator; 4 import java.util.Objects; 5 import java.util.Spliterator; 6 import java.util.Spliterators; 7 import java.util.function.Consumer; 8 9 public interface Iterable<T> { 10 Iterator<T> iterator(); 11 12 default void forEach(Consumer<? super T> action) { 13 Objects.requireNonNull(action); 14 for (T t : this) { 15 action.accept(t); 16 } 17 } 18 19 default Spliterator<T> spliterator() { 20 return Spliterators.spliteratorUnknownSize(iterator(), 0); 21 } 22 }
能夠看到它接收一個 java.util.function.Consumer 類型參數,查看 JavaDoc:
該類上標註了一個 @FunctionalInterface 註解,而且註釋中還說明了這個接口是一個函數式接口。查看該註解的 JavaDoc:
能夠得到以下信息:
看以下示例:
package zze.java8; @FunctionalInterface interface MyInterface { void test(); } public class Test2 { public void test(MyInterface myInterface) { System.out.println("Test2.test"); myInterface.test(); } public static void main(String[] args) { Test2 test2 = new Test2(); test2.test(() ->System.out.println("MyInterface.test")); /* * Test2.test * MyInterface.test */ } }
能夠看到, Test2 的實例方法 test 其實是接收一個 MyInterface 對象,而 MyInterface 是一個函數式接口,因此咱們能夠經過傳入 Lambda 表達式做爲 myInterface 參數,即:此時傳入的 Lambda 表達式就是對函數函數式接口 MyInterface 的實現對象。因此上面代碼其實也能夠修改成以下:
Test2 test2 = new Test2(); MyInterface myInterface = () -> System.out.println("MyInterface.test"); test2.test(myInterface); System.out.println(myInterface.getClass()); System.out.println(myInterface.getClass().getSuperclass()); System.out.println(myInterface.getClass().getInterfaces()[0]); /* Test2.test MyInterface.test class zze.java8.Test2$$Lambda$1/990368553 class java.lang.Object interface zze.java8.MyInterface */
總結: