天天看點

Lambda表達式,真爽!

簡介

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              }