1.什麼是lambda表達式?
首先看一段比較熟悉的代碼片斷,目的是爲按鈕註冊一個事件監聽器,當按鈕被點擊時輸出一個提示信息"button clicked",java
Button button = new Button(); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("button clicked"); } });
這段自己沒有什麼問題,可是代碼量相對有點多並且語義表達上也不夠清新。那麼什麼樣的表達纔是清晰的呢?Jdk8給我帶來了一種全新的思路,可讓咱們更加優雅的實現上述功能,咱們看下述Lambda代碼。express
Button button = new Button(); button.addActionListener(e-> System.out.println("button clicked"));
2.lambda 表達式3種表現形式
無參數表達式: () -> System.out.println(「hello world!」) 單個參數表達式 : (String sayHello) -> System.out.println(sayHello) 多個參數表達式: (int x, int y) -> x + y
須要注意的是以上三種基本形式能夠根據「是否有顯示聲明參數類型」、「是否有返回值」、「是否省略()」等又會衍生出若干變化。數組
3.lambda表達式值引用
lambda表達式操做的是對象的引用值而不是對象自己,怎麼理解呢?匿名內部類若是引用所在方法中變量那麼該變量必須聲明爲final類型,也就是對象只能對賦值一次。相似的lambda表達式引用所在方法中的變量也必須是final類型,表現形式上能夠顯示聲明final 也能夠不顯示聲明,可是該變量必須是一個既成事實上的final變量。ide
public static void sayHello(String name) { if (Objects.isNull(name)) { name = "kity"; } ActionListener act = event -> System.out.println("hello " + name); .... }
上述代碼變異會提示 Variable used in lambda expression should be final or effectively final。函數
4.lambda表達式方法引用
之前咱們關注更多的是對象引用,jdk8以後新增了方法引用,須要使用關鍵字 「::」,這是jdk8咱們須要重點關注的。一下咱們詳細列舉6種方法引用的詳細格式。this
- 靜態方法引用
格式 類名::靜態方法名spa
注意事項:
被引用方法參數列表和函數式接口中方法的參數一致
接口的抽象方法沒有返回值,引用的方法能夠有返回值也能夠沒有
接口的抽象方法有返回值,引用方法必須有同類型的返回值code
interface Transfer { int run(String string); } public static void main(String[] args) { printStr("1", Integer::valueOf); } public static void printStr(String str, Transfer t) { int num = t.run(str); System.out.println(num); }
- 對象方法引用
格式 對象::方法名orm
@FunctionalInterface interface Action { String run(Integer num); } public static void main(String[] args) { Action t2 = new Person()::walking; System.out.println(t2.run(5)); } class Person { public String walking(Integer num) { return "go ".concat(String.valueOf(num)).concat(" step ..."); } }
- 構造方法引用
格式 類名::new對象
interface Action { Person init(String string); } class Person { String name; public Person(String name) { this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } public static void main(String[] args) { Action t2 = Person::new; Person p2 = t2.init("李四"); System.out.println(p2); }
- 數組構造方法引用
格式 數據類型[]::new
interface Action { String[] run(int len); } public static void main(String[] args) { Action action = String[]::new; String[] arr = action.run(5); System.out.println(arr.length); }
- 特定類型的方法引用
格式 類名::非靜態方法
ArrayList<String> list = new ArrayList<>(); Collections.addAll(list, "a", "C", "B", "d"); Collections.sort(list, String::compareToIgnoreCase); System.out.println(list);
- 類中方法調用父類或本類方法引用
格式 this::方法名
super::方法名
interface Action { void init(); } class Father { public void buy() { System.out.println("買東西"); } } class Son extends Father { public void buy() { System.out.println("買糖"); } public void run() { Action action1 = this::buy; action1.init(); Action action2 = super::buy; action2.init(); } } public static void main(String[] args) { Son s = new Son(); s.run(); }
5.lambda函數接口
函數式接口是 Java8 引入的一個新特性,是一種特殊的接口:SAM類型的接口(Single Abstract Method),但本質上仍是接口。相比較於其餘接口,函數式接口有且只能有一個抽象方法。只要接口中出現多個抽象方法,那麼就不能稱之爲函數式接口,運行的時候就會報錯。爲此 Java8 提供了一個新的註解@FunctionalInterface,若是接口被這個註解標註,就說明該接口是函數式接口,若是有多於一個的抽象方法,在編譯的時候就會報錯。可是這個註解不是必需的,只要接口符合函數式接口的定義,那麼這個接口就是函數式接。
Java中主要的函數接口
接口 | 參數 | 返回類型 | 描述 |
Predicate<T> | T | boolean | 斷言型接口 |
Consumer<T> | T | void | 消費型接口 |
Function<T,R> | T | R | 功能型接口 |
Supplier<T> | None | T | 供給型接口 |
UnaryOperator<T> | T | T | 一元運算 |
BinaryOperator<T> | (T,T) | T | 二元運算 |
6.lambda函數類型推斷
Lamda表達式是函數式接口的一個實例,然而咱們並無從Lambda表達式中看到有關函數式接口的任何信息,於是有必要弄清楚Lambda的類型是什麼?它實現了哪一個函數式接口?其實Lambda表達式的類型是從上下文推斷出來的,這裏的上下文包括以下3種: 賦值上下文 方法調用上下文(參數與返回值) 類型轉換上下文 經過這3種上下文就能夠推斷出Lambda表達式的目標類型(與之對應的函數式接口)。