1. 程式人生 > 程式設計 >java8之lambda表示式用法總結

java8之lambda表示式用法總結

java8之lambda表示式

目的:行為引數化

Lambda表示式是簡潔地表示可傳遞的匿名函式的一種方式:它沒有名稱,但它有引數列表、函式主體、返回型別,可能還有一個可以丟擲的異常列表。

Lambda的基本語法是(parameters) -> expression 或 (parameters) -> { statements; }。其中, (parameters) -> expression 的表示式中隱含了return,如 () -> 42; (parameters) -> { statements; } 的花括號內是語句。

舉例:

() -> 42  //引數為空,返回一個int
(List<String> list) -> list.isEmpty() //引數為list,返回一個boolean
(int x,int y) -> x*y //引數為兩個int,返回一個int
(String s) -> System.out.println(s); //引數為一個String,不返回結果
(String s) -> {System.out.println(s);} //引數為一個String,列印字串

哪些地方使用哪些lambda

函式式介面是隻定義一個抽象方法的介面,即使擁有多個預設方法。FunctionalInterface 標註一個函式式介面,會加入編譯檢查。函式式介面中預設方法的目的是:改變已釋出的介面而不破壞已有的實現。

在接受函式式介面為引數的地方,都可以使用lambda表示式。

例子:

public void execute(Runnable r){
  r.run(); 
}
execute(() -> {}); //使用lambda,Runnable是引數為空,沒有返回值的函式式介面,即() -> void 
//fetch返回一個函式式介面,() -> String
public Callable<String> fetch() { 
  return () -> "Tricky example ;-)";
}

為什麼只有在函式式介面的地方使用呢?lambda表示式沒有函式名,只有引數列表,函式主體和返回值,如果介面有多個方法,就不能直接匹配到正確的方法上了,所以,只有一個抽象方法的函式式介面可以滿足。

Predicate

java.util.function.Predicate<T>是一個含有多個預設方法的函式式介面,抽象方法為:(T t) -> bool。看下程式碼,你就懂了~

FunctionalInterface
public interface Predicate<T> {
  //介面方法,入參為泛型T,返回bool。即:(T t) -> bool
  boolean test(T t);
  //預設方法,and操作
  default Predicate<T> and(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) && other.test(t);
  }
  //預設方法,取反操作
  default Predicate<T> negate() {
    return (t) -> !test(t);
  }
  //預設方法,or 操作
  default Predicate<T> or(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) || other.test(t);
  }
  //預設方法,判斷是否相等
  static <T> Predicate<T> isEqual(Object targetRef) {
    return (null == targetRef)
        ? Objects::isNull
        : object -> targetRef.equals(object);
  }

使用

Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();

Consumer

java.util.function.Consumer<T> 是一個只含有一個預設方法的函式式介面,抽象方法為:(T t) ->void。看下程式碼,你就懂了~

@FunctionalInterface
public interface Consumer<T> {
  //介面方法,入參為泛型T,返回void。即:(T t) -> void
  void accept(T t);
  //預設方法,可以執行級聯操作
  default Consumer<T> andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);
    return (T t) -> { accept(t); after.accept(t); };
  }

Function

java.util.function.Function<T,R>是一個含有多個預設方法的函式式介面,抽象方法為:(T t) ->R。看下程式碼,你就懂了~

@FunctionalInterface
public interface Function<T,R> {
  //介面方法,入參為泛型T,返回泛型R。即:(T t) -> R
  R apply(T t);
  //預設方法,實現級聯操作。before方法輸入V,輸出T,本function輸入T,輸出R。
  default <V> Function<V,R> compose(Function<? super V,? extends T> before) {
    Objects.requireNonNull(before);
    return (V v) -> apply(before.apply(v));
  }
  //預設方法,級聯操作
  default <V> Function<T,V> andThen(Function<? super R,? extends V> after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
  }
  //預設方法,輸入啥,輸出啥
  static <T> Function<T,T> identity() {
    return t -> t;
  }

特定方法避免裝箱操作

在處理資料時,使用特定方法,可以避免裝箱操作,如:IntPredicate、LongConsumer、DoubleFunction等。具體見API庫。

總結

函式描述符 函式式介面
(T) ->bool java.util.function.Predicate<T>
(T) -> void java.util.function.Consumer<T>
(T) -> R java.util.function.Function<T,R>
(T,U) -> R java.util.function.BiFunction<T,U,R>
() -> T java.util.function.Supplier<T>

只要函式描述符相容,函式式介面就可以複用。

特殊的void相容規則:

// Predicate返回了一個boolean 
Predicate<String> p = s -> list.add(s); 
// Consumer返回了一個void 
Consumer<String> b = s -> list.add(s);

方法引用

方法引用是呼叫單一方法的Lambda的快捷寫法,格式ClassName::methodName。看下栗子你就懂了~

一般方法引用

(Apple a) -> a.getWeight() 等價於Apple::getWeight 
() -> Thread.currentThread().dumpStack() 等價於 Thread.currentThread()::dumpStack
(str,i) -> str.substring(i) 等價於 String::substring
(String s) -> System.out.println(s) 等價於 System.out::println
(list,element) -> list.contains(element) 等價於 List::contains

主要有三類:

  1. 指向靜態方法的方法引用,如:Integer::parseInt
  2. 指向任意型別例項方法的方法引用,如:String::length
  3. 指向現有物件的例項方法的方法引用,如:User::getUserId

建構函式引用

無參建構函式

無參建構函式的函式描述符:() -> T,由上面的總結知,可以使用Supplier介面,如下:

Supplier<User> c1 = User::new; // c1 = () -> new User();
User user = c1.get();

有一個引數的建構函式

有一個引數的建構函式的函式描述符是(T) -> R,可以使用Function介面,如下:

Function<Long,User> c2 = User::new;

User user = c2.apply(110L);

有三個引數的建構函式

有三個引數的建構函式的函式描述符是(T,V) -> R,沒有現成的介面,需要自定義,如下:

@Data
@AllArgsConstructor
public class User {
  private String name;
  private Long userId;
  private Integer age;
}
@FunctionalInterface
public interface TriFunction<T,V,R> {
  R create(T t,U u,V v);
}
public static void main(String[] args) {
  TriFunction<String,Long,Integer,User> triFunction = User::new;
  User user = triFunction.create("tina",12L,13);
}

使用注意事項

Lambda表示式可以引用靜態變數、成員變數和最終的(final) 或事實上最終的區域性變數。

更多關於java8中lambda表示式相關方法請檢視下面的相關連結