Lambda 表達式是一種匿名函數,簡單地說,它是沒有聲明的方法,也即沒有訪問修飾符、返回值聲明和名字。html
它能夠寫出更簡潔、更靈活的代碼。做爲一種更緊湊的代碼風格,使 Java 語言的表達能力獲得了提高。java
基本語法: (parameters) -> expressionexpress
或者:(parameters) ->{ statements; 數組
舉例說明:app
// 1. 不須要參數,返回值爲 5 () -> 5 // 2. 接收一個參數(數字類型),返回其2倍的值 x -> 2 * x // 3. 接受2個參數(數字),並返回他們的差值 (x, y) -> x – y // 4. 接收2個int型整數,返回他們的和 (int x, int y) -> x + y // 5. 接受一個 string 對象,並在控制檯打印,不返回任何值(看起來像是返回void) (String s) -> System.out.print(s)
再對上面進行舉例說明以前,必須先來理解下函數式接口,由於Lambda是創建在函數式接口的基礎上的。ide
記住!
函數
(1)只包含一個抽象方法的接口,稱爲函數式接口。post
(2)你能夠經過 Lambda 表達式來建立該接口的對象。this
(3)咱們能夠在任意函數式接口上使用 @FunctionalInterface 註解,這樣作能夠檢測它是不是一個函數式接口,同時 javadoc 也會包含一條聲明,說明這個接口是一個函數式接口。url
在實際開發者有兩個比較常見的函數式接口:Runnable接口,Comparator接口
先舉例Runnable接口相關
public class Test { public static void main(String[] args) { // 1.1使用匿名內部類 new Thread(new Runnable() { @Override public void run() { System.out.println("Hello world !"); } }).start(); // 1.2使用 lambda 得到Runnable接口對象 new Thread(() -> System.out.println("Hello world !")).start(); //============================================================================= // 2.1使用匿名內部類 Runnable race1 = new Runnable() { @Override public void run() { System.out.println("Hello world !"); } }; // 2.2使用 lambda直接得到接口對象 Runnable race2 = () -> System.out.println("Hello world !"); // 直接調用 run 方法(沒開新線程哦!) race1.run(); race2.run(); } } /*輸出結果 * Hello world ! * Hello world ! * Hello world ! * Hello world ! */
經過上面案例能夠看出:經過Lambda表達式看去舒服清爽多了,2而經過匿名內部類代碼老是不夠整潔。
再舉一個例子:使用Lambda對數組排序
public class TestArray { public static void main(String[] args) { String[] players = {"zhansgan", "lisi", "wangwu", "zhaoliu", "wangmazi"}; // 1.1 使用匿名內部類根據 surname 排序 players Arrays.sort(players, new Comparator<String>() { @Override public int compare(String s1, String s2) { return (s1.compareTo(s2)); } }); // 1.2 使用 lambda 排序,根據 surname Arrays.sort(players, (String s1, String s2) -> s1.compareTo(s2)); //================================================================================================ // 2.1 使用匿名內部類根據 name lenght 排序 players Arrays.sort(players, new Comparator<String>() { @Override public int compare(String s1, String s2) { return (s1.length() - s2.length()); } }); // 2.2使用Lambda,根據name length Arrays.sort(players, (String s1, String s2) -> (s1.length() - s2.length())); //================================================================================================== // 3.1 使用匿名內部類排序 players, 根據最後一個字母 Arrays.sort(players, new Comparator<String>() { @Override public int compare(String s1, String s2) { return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)); } }); // 3.2 使用Lambda,根據最後一個字母 Arrays.sort(players, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1))); } }
經過上面例子咱們再來思考爲何Lambda表達式須要函數式接口?其實很簡單目的就是爲來保證惟一。
你的Runnable接口只要一個抽象方法,那麼我用() -> System.out.println("Hello world !"),就只能表明run方法,若是你下面還有一個抽象方法,那我使用Lambda表達式,
那鬼才知道要調用哪一個抽象方法呢。
首先注意:方法引用,不是方法調用!方法引用,不是方法調用!方法引用,不是方法調用!
函數式接口的實例能夠經過 lambda 表達式、 方法引用、構造方法引用來建立。方法引用是 lambda 表達式的語法糖,任何用方法引用的地方均可由lambda表達式替換,
可是並非全部的lambda表達式均可以用方法引用來替換。
舉例
這就是一個打印集合全部元素的例子,value -> System.out.println(value) 是一個Consumer函數式接口, 這個函數式接口能夠經過方法引用來替換。
public class TestArray { public static void main(String[] args) { List<String> list = Arrays.asList("xuxiaoxiao", "xudada", "xuzhongzhong"); list.forEach(value -> System.out.println(value)); } /* 輸出: * xuxiaoxiao * xudada * xuzhongzhong */ }
使用方法引用的方式,和上面的輸出是同樣的,方法引用使用的是雙冒號(::)
list.forEach(System.out::println);
類別 | 使用形式 |
---|---|
靜態方法引用 | 類名 :: 靜態方法名 |
實例方法引用 | 對象名(引用名) :: 實例方法名 |
類方法引用 | 類名 :: 實例方法名 |
構造方法引用 | 類名 :: new |
(1)靜態方法引用
public class Apple { private String name; private String color; private double weight; public Apple(String name, String color, double weight) { this.name = name; this.color = color; this.weight = weight; } public static int compareByWeight(Apple a1, Apple a2) { double diff = a1.getWeight() - a2.getWeight(); return new Double(diff).intValue(); } //還有getter setter toString }
有一個蘋果的List,如今須要根據蘋果的重量進行排序。List 的 sort 函數接收一個 Comparator 類型的參數,Comparator 是一個函數式接口,接收兩個參數,
返回一個int值。Apple的靜態方法compareByWeight正好符合Comparator函數式接口,因此可使用:
Apple::compareByWeight 靜態方法引用來替代lambda表達式
public class LambdaTest { public static void main(String[] args) { Apple apple1 = new Apple("紅富士", "Red", 280); Apple apple2 = new Apple("馮心", "Yello", 470); Apple apple3 = new Apple("大牛", "Red", 320); Apple apple4 = new Apple("小小", "Green", 300); List<Apple> appleList = Arrays.asList(apple1, apple2, apple3, apple4); //lambda 表達式形式 //appleList.sort((Apple a1, Apple a2) -> { // return new Double(a1.getWeight() - a2.getWeight()).intValue(); //}); //靜態方法引用形式(能夠看出引用方法比上面的更加簡單 appleList.sort(Apple::compareByWeight); appleList.forEach(apple -> System.out.println(apple)); } } 輸出: Apple{category='紅富士', color='Red', weight=280.0} Apple{category='小小', color='Green', weight=300.0} Apple{category='大牛', color='Red', weight=320.0} Apple{category='馮心', color='Yello', weight=470.0}
注意:Apple.compareByWeight是方法的調用,而Apple::compareByWeight方法引用,這二者徹底不是一回事。
(2)實例方法引用
這個compareByWeight是一個實例方法
public class AppleComparator { public int compareByWeight(Apple a1, Apple a2) { double diff = a1.getWeight() - a2.getWeight(); return new Double(diff).intValue(); } }
下面的例子經過實例對象的方法引用 comparator::compareByWeight 來代替lambda表達式
public class LambdaTest { public static void main(String[] args) { Apple apple1 = new Apple("紅富士", "Red", 280); Apple apple2 = new Apple("馮心", "Yello", 470); Apple apple3 = new Apple("哈哈", "Red", 320); Apple apple4 = new Apple("小小", "Green", 300); List<Apple> appleList = Arrays.asList(apple1, apple2, apple3, apple4); //lambda 表達式形式 //appleList.sort((Apple a1, Apple a2) -> { // return new Double(a1.getWeight() - a2.getWeight()).intValue(); //}); //實例方法引用 AppleComparator comparator = new AppleComparator(); appleList.sort(comparator::compareByWeight); appleList.forEach(apple -> System.out.println(apple)); } } 輸出: Apple{category='紅富士', color='Red', weight=280.0} Apple{category='小小', color='Green', weight=300.0} Apple{category='哈哈', color='Red', weight=320.0} Apple{category='馮心', color='Yello', weight=470.0}
經過上面兩個例子能夠看到,靜態方法引用和實例方法引用都是比較好理解的。
(3)類方法引用
通常來講,同類型對象的比較,應該當前調用方法的對象與另一個對象進行比較,好的設計應該像下面:
public class Apple { private String category; private String color; private double weight; public Apple(String category, String color, double weight) { this.category = category; this.color = color; this.weight = weight; } //這裏和上面靜態方式惟一區別就是這個參數就一個,須要實例對象調這個方法 public int compareByWeight(Apple other) { double diff = this.getWeight() - other.getWeight(); return new Double(diff).intValue(); } //getter setter toString }
仍是以前List排序的例子,看看使用類方法引用如何寫:
public class LambdaTest { public static void main(String[] args) { Apple apple1 = new Apple("紅富士", "Red", 280); Apple apple2 = new Apple("黃元帥", "Yello", 470); Apple apple3 = new Apple("紅將軍", "Red", 320); Apple apple4 = new Apple("國光", "Green", 300); List<Apple> appleList = Arrays.asList(apple1, apple2, apple3, apple4); //lambda 表達式形式 //appleList.sort((Apple a1, Apple a2) -> { // return new Double(a1.getWeight() - a2.getWeight()).intValue(); //}); //這裏是類方法引用 appleList.sort(Apple::compareByWeight); appleList.forEach(apple -> System.out.println(apple)); } } 輸出: Apple{category='紅富士', color='Red', weight=280.0} Apple{category='國光', color='Green', weight=300.0} Apple{category='紅將軍', color='Red', weight=320.0} Apple{category='黃元帥', color='Yello', weight=470.0}
這裏使用的是:類名::實例方法名。首先要說明的是,方法引用不是方法調用。compareByWeight必定是某個實例調用的,就是lambda表達式的第一個參數,而後lambda
表達式剩下的參數做爲 compareByWeight的參數,這樣compareByWeight正好符合lambda表達式的定義。
或者也能夠這樣理解:
(Apple a1, Apple a2) -> { return new Double(a1.getWeight() - a2.getWeight()).intValue(); }
int compareByWeight(Apple other) 須要當前對象調用,而後與另一個對象比較,而且返回一個int值。能夠理解爲lambda表達式的第一個參數 a1 賦值給當前對象, 而後 a2
賦值給 other對象,而後返回int值。
(4)構造方法引用
public class ConstructionMethodTest { public String getString(Supplier<String> supplier) { return supplier.get(); } public static void main(String[] args) { ConstructionMethodTest test = new ConstructionMethodTest(); //lambda表達式形式 System.out.println(test.getString(() -> { return new String();})); //構造方法引用形式 System.out.println(test.getString(String::new)); } }
getString 方法接收一個Supplier類型的參數,Supplier 不接收參數,返回一個String。lambda表達式應該這樣寫:
() -> { return new String();}
替換成方法引用的形式以下: 實際上調用的是String 無參構造方法。
String::new
想太多,作太少,中間的落差就是煩惱。想沒有煩惱,要麼別想,要麼多作。中校【9】