# 簡介
JDK的更新的目的有以下幾個:增加新的功能、修複bug、性能優化、簡化代碼等幾個方面,Lambda表達式就是屬于簡化代碼,用于簡化匿名實作類,提供一種更加簡潔的寫法。Lambda表達式在Swift語言中稱之為代碼塊,Lambda表達式可以認為是一種特殊的接口,該接口必須隻有一個抽象方法。
文法
(參數類型 參數名, 數參數類型 參數名2...) -> { // code};
小括号()中的内容就是方法中的參數清單包括參數類型、參數名,其中參數類型是可以省略的,當參數個數隻有一個時也可以省略掉小括号;
花括号{}中的内容就是方法中的方法體,當方法體中隻有一行代碼時可以省略{},當方法體中隻有一行代碼并且需要傳回值時也可以省略掉return;
由于Lambda表達式是匿名實作類的簡寫,是一種特殊的接口,當指派給一個變量時也少不掉分号;
Lambda表達式的作用
- 簡化匿名實作類的書寫
- 作為函數中的參數來傳遞
# 示例
示例1:兩個參數,一個傳回值
IHello 一個很普通的接口,但接口中隻能有一個抽象方法
public interface IHello {String sayHello(String name, String msg);}
Main
public class Main {public static void main(String[] args) {// 将一個Lambda表達式指派給一個接口,說明Lambda表達式就是一種接口資料類型,隻不過該接口隻能有一個抽象方法// 參數清單可以省略參數類型,可以寫成(name, msg),// 在JDK1.8中有個叫做類型推斷的東西,可以自動推斷出參數的類型,// 因為IHello中隻有一個抽象方法,知道方法了就知道參數清單了,進而就能推出參數類型來IHello iHello = (String name, String msg) -> {String hello = name + ": " + msg;return hello;};// 調用接口的方法String content = iHello.sayHello("mengday", "happy new year everyone!");System.out.println(content);}}
示例2:一個參數,一個傳回值
public interface IHello {String sayHello(String name);}
public class Main {public static void main(String[] args) {// 參數清單可以省略參數類型,當隻有一個參數時可以省略小括号 (String name) --> (name) --> name// 當方法體中隻有一行代碼并且有傳回值時可以同時省略花括号和return// { return name + ": " + "happy new year everyone!";} --> name + ": " + "happy new year everyone!";IHello iHello = name -> name + ": " + "happy new year everyone!";
String content = iHello.sayHello("mengday");System.out.println(content);}}
示例3:沒有參數,沒有傳回值
public interface IHello {void sayHello();}
main
public class Main {public static void main(String[] args) {// 當表達式沒有參數時一對小括号是不能省略掉的IHello iHello = () -> System.out.println("mengday: happy new year everyone!");iHello.sayHello();}}
從這三個示例中我們發現我們隻定義了接口,并沒有定義實作類,而是通過Lambda表達式來代替了實作類。
注意:Lambda接口隻能有一個抽象方法,可以同時擁有多個靜态方法和預設方法。
示例4:Lambda表達式參數
public interface IHello { void sayHello(String name);}
public class Main {
public static void sayHello(IHello iHello, String name) {iHello.sayHello(name);}
public static void main(String[] args) {
IHello iHello = name -> {String content = name + ": " + "happy new year everyone!";System.out.println(content);};
// 這裡可以把iHelo看成一個匿名實作類來傳遞參數sayHello(iHello, "mengday");
// 如果去掉變量的接收,直接将Lambda表達式傳遞到參數中,此時Lambda表達式更像是一個函數// 也就是說JDK1.8竟然可以将一個函數作為參數傳遞到方法中,這是之前版本做不到的// 将函數作為方法的參數傳遞,一般用于回調函數,将回調函數傳遞到方法中sayHello(name -> {String content = name + ": " + "happy new year everyone!";System.out.println(content);}, "mengday");}}
示例5:集合排序示例
public static void main(String[] args) {
// 寫法一:使用匿名内部類// 好好學習,天天向上List<String> words = Arrays.asList("good", "good", "study", "day", "day", "up");
// public staticCollections.sort(words, new Comparator<String>() {@Overridepublic int compare(String s1, String s2) {// 降續排序return s2.compareTo(s1);}});
System.out.println(words);
// 寫法二:使用Lambda表達式// 咱倆誰跟誰words = Arrays.asList("we", "two", "who", "and", "who");Collections.sort(words, (String s1, String s2) -> {return s2.compareTo(s1);});System.out.println(words);
// 寫法三:使用Lambda表達式(簡寫)// 有事起奏,無事退朝words = Arrays.asList("if", "you", "have", "something", "to", "say", "then", "say!","if", "you", "have", "nothing", "to", "say", "go", "home!");Collections.sort(words, (s1, s2) -> s2.compareTo(s1));System.out.println(words);}
# 函數式接口@FunctionalInterface
從我們自定義的IHello示例來看,Lambda表達式其實是一種接口類型的資料類型,嚴格的說Lambda表達式的資料類型是:函數式接口,是一種特殊的接口,該接口使用@FunctionalInterface注解來标記(不是必須的,可以不用該注解标記,IHello接口就沒有使用該注解标記, ),并且接口中隻能有一個抽象方法,可以有多個靜态方法或者預設方法, 每一個該類型的lambda表達式都會被比對到這個抽象方法。
@FunctionalInterfacepublic interface Comparator<T> {int compare(T o1, T o2);// 其它static、default方法}
@FunctionalInterface: 該注解沒啥太大含義,該注解是給編譯器做檢查使用的,如果使用了該注解,編譯器就會檢查該接口中的抽象方法是不是隻有一個,如果有多個就會報錯:在接口Xxx中找到多個非覆寫抽象方法
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface FunctionalInterface {}
我們完善一下我們的IHello, 使用@FunctionalInterface注解
@FunctionalInterfacepublic interface IHello {void sayHello(String name);}
我們可以将lambda表達式當作任意隻包含一個抽象方法的接口類型,也就是說我們的IHello接口無論叫什麼名字,接口中的方法無論叫什麼名字都無所謂(隻是可讀性更好些),是以可以再進行抽象化一下,JDK1.8中提供了這樣的函數式接口,我們也不需要再定義IHello接口了,JDK1.8中提供了Supplier、Consumer、Function、BiFunction,這幾個是比較常用的
# Supplier< T > 供應商:沒有參數,有傳回值
@FunctionalInterfacepublic interface Supplier{T get();}
# Consumer< T > 消費者:隻有一個參數,沒有傳回值
@FunctionalInterfacepublic interface Consumer<T> {void accept(T t);}
# Function< T, R > 函數:一個參數,一個傳回值
@FunctionalInterfacepublic interface Function<T, R> {R apply(T t);}
# BiFunction< T, U, R > 二進制函數:兩個參數,一個傳回值
@FunctionalInterfacepublic interface BiFunction<T, U, R> {R apply(T t, U u);}
# Comparator< T > 比較器:接收兩個參數,傳回比較的結果
@FunctionalInterfacepublic interface Comparator<T> {int compare(T o1, T o2);}
使用以上四大函數式接口來取代自定義的接口IHello
public class Main {private static String end = ".";
public static void main(String[] args) {// 直接使用JDK1.8提供的接口,不需要再定義IHello接口, 直接使用JDK提供的接口來接收Lambda表達式Supplier<String> supplier = () -> "mengday: happy new year everyone!";String result = supplier.get();System.out.println(result);
Consumer<String> consumer = (name) -> System.out.println(name + ": " + "happy new year everyone!");consumer.accept("mengday");
Function<String, String> func = (name) -> name + ": " + "happy new year everyone!";String hi = func.apply("mengday");System.out.println(hi);
// 在代碼塊的内部可以通路靜态全局變量// 在代碼塊中可以通路外邊局部變量// 在代碼塊的内部可以修改全局靜态變量// 在代碼塊内部是不能通路接口中的其它方法的String split = ": ";BiFunction<String, String, String> biFunction = (String name, String msg) -> {end = "!";String hello = name + split + msg + end;return hello;};String hello = biFunction.apply("mengday", "happy new year everyone");System.out.println(hello);
// 根據字元串長度比較大小Comparator<String> comparator = (s1, s2) -> s1.length() - s2.length();int compare = comparator.compare("abc", "ab");System.out.println(compare);}}
# Predicate< T > 斷言 謂詞: 用于測試一個條件的真假
package java.util.function;import java.util.Objects;
@FunctionalInterfacepublic interface Predicate<T> {// 在給定的參數上評估這個謂詞boolean test(T t);
// 傳回一個組合的謂詞,表示該謂詞與另一個謂詞的短路邏輯ANDdefault Predicate{Objects.requireNonNull(other);return (t) -> test(t) && other.test(t);}
// 傳回表示此謂詞的邏輯否定的謂詞,相當于notdefault Predicate{return (t) -> !test(t);}
// 傳回一個組合的謂詞,表示該謂詞與另一個謂詞的短路邏輯或default Predicate{Objects.requireNonNull(other);return (t) -> test(t) || other.test(t);}
// 傳回根據 Objects.equals(Object, Object)測試兩個參數是否相等的謂詞static return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object);}}
public static void main(String[] args) {// 可以構造複雜的條件:并且and、或者or、否negateString email = "[email protected]";Predicate<String> predicate = (str) -> str.length() > 20;
// 測試 emial.length > 0 的booleanboolean result = predicate.test(email); // false
// 測試 !(emial.length > 0) 的booleanresult = predicate.negate().test(email); // true
Predicate<String> orPredicate = (str) -> str.contains("@");// 測試 emial.length > 0 or emial.contains("@")result = predicate.or(orPredicate).test(email); // true}