簡介
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 static <T> void sort(List<T> list, Comparator<? super T> c) Collections.sort(words, new Comparator<String>() { @Override public 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表達式都會被比對到這個抽象方法。
@FunctionalInterface public 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注解
@FunctionalInterface public interface IHello { void sayHello(String name); }
我們可以将lambda表達式當作任意隻包含一個抽象方法的接口類型,也就是說我們的IHello接口無論叫什麼名字,接口中的方法無論叫什麼名字都無所謂(隻是可讀性更好些),是以可以再進行抽象化一下,JDK1.8中提供了這樣的函數式接口,我們也不需要再定義IHello接口了,JDK1.8中提供了Supplier、Consumer、Function、BiFunction,這幾個是比較常用的
Supplier< T > 供應商:沒有參數,有傳回值
@FunctionalInterface public interface Supplier<T> { T get(); }
Consumer< T > 消費者:隻有一個參數,沒有傳回值
@FunctionalInterface public interface Consumer<T> { void accept(T t); }
Function< T, R > 函數:一個參數,一個傳回值
@FunctionalInterface public 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 > 比較器:接收兩個參數,傳回比較的結果
@FunctionalInterface public 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; @FunctionalInterface public interface Predicate<T> { // 在給定的參數上評估這個謂詞 boolean test(T t); // 傳回一個組合的謂詞,表示該謂詞與另一個謂詞的短路邏輯AND default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } // 傳回表示此謂詞的邏輯否定的謂詞,相當于not default Predicate<T> negate() { return (t) -> !test(t); } // 傳回一個組合的謂詞,表示該謂詞與另一個謂詞的短路邏輯或 default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } // 傳回根據 Objects.equals(Object, Object)測試兩個參數是否相等的謂詞 static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
public static void main(String[] args) { // 可以構造複雜的條件: 并且and、或者or、否negate String email = "[email protected]"; Predicate<String> predicate = (str) -> str.length() > 20; // 測試 emial.length > 0 的boolean boolean result = predicate.test(email); // false // 測試 !(emial.length > 0) 的boolean result = predicate.negate().test(email); // true Predicate<String> orPredicate = (str) -> str.contains("@"); // 測試 emial.length > 0 or emial.contains("@") result = predicate.or(orPredicate).test(email); // true }