Java8(1)之Lambda表達式初步與函數式接口

Lambda表達式初步

介紹

什麼是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 }
例 1:

如上代碼很簡單,經過第 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:

在例 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.lang.Iterable

能夠看到它接收一個 java.util.function.Consumer 類型參數,查看 JavaDoc:

該類上標註了一個 @FunctionalInterface 註解,而且註釋中還說明了這個接口是一個函數式接口。查看該註解的 JavaDoc:

能夠得到以下信息:

  • 這是一個信息性註解,用於標註在一個接口上使一個接口成爲函數式接口。
  • 從概念上來講,一個函數式接口有且只能有一個抽象方法。
  • 若是在接口中僅定義了一個抽象方法而且它覆蓋了 java.lang.Object 類中公開的方法,那麼這個接口就不是函數式接口。
  • 函數式接口的實例能夠經過 Lambda 表達式、方法引用或構造方法引用來建立。
  • 若是被該註解標註的類型不知足下面兩個條件,那麼編譯器將生成錯誤信息。
    1. 該類型是一個接口,不能夠是註解類型、枚舉、類。
    2. 被註解類型必須知足函數式註解的要求。
  • 若是一個接口知足了函數式接口的要求,但它沒有標註該註解,編譯器也會將它看做是函數式接口。

Lambda表達式的做用

看以下示例:

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
*/

總結:

  • Lambda 表達式爲 Java 添加了缺失的函數式編程特性,使咱們能將函數當作一等公民看待。
  • 在將函數做爲一等公民的語言中(如 Python、JavaScript),Lambda 表達式的類型是函數。但在 Java 中,Lambda 表達式是一個對象,它們必須依附於一類特別的對象類型——函數式接口(functional interface)
相關文章
相關標籤/搜索