Java8 新特性與 lambda 表達式筆記

java8 的接口新特性

先看看下面的接口的默認方法和靜態(tài)方法,感受一下:

public interface Java8Interface {
  //默認方法
  default String createName() {
    //邏輯實現(xiàn)
    return "xxxx";
  }
  //靜態(tài)方法
  public static LocalDateTime getLocalDate() {
    return LocalDateTime.now();
  }
}
  1. 默認實現(xiàn)方法

如果多繼承狀態(tài)下有默認方法,遵循 3 條規(guī)則

  • 類勝與接口,如果在繼承鏈中有方法體或抽象的方法聲明,可以忽略接口中的定義

  • 子類勝于父類。如果一個接口繼承了另外一個接口,而且兩個接口都定義了相同的默認方法,那么子類中的默認方法勝出

  • 如上都不適用,要么子類實現(xiàn)該方法,要么將方法聲明為抽象方法

  1. 靜態(tài)方法

為了幫助編寫類庫的開發(fā)人員,Java 8 增加類接口靜態(tài)方法新特性

lambda 表達式

函數(shù)式接口

函數(shù)式接口是 lambda 表達式的類型,其特點是只有一個抽象方法的接口,但是可以有 default 類型的方法實現(xiàn)。

JDK 提供幾個常用的函數(shù)式接口

  1. Predicate<T>:通過傳入一個對象T,返回是否匹配

@FunctionalInterface //這個注解是作用是為了標識該接口是函數(shù)式接口,編譯時可以檢查語法是否準確

public interface Predicate<T> {
  //唯一一個抽象方法
  boolean test(T t);
  //其他 default 的方法實現(xiàn)省略。。。
}
  1. Consumer<T>:傳入對象 T,沒有返回值,是一種消費型接口

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}
  1. Function<T,R> :一個入?yún),返回值 R

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}
  1. Supplier<T> : 沒入?yún)?,返?T

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

lambda 表達式語法

(參數(shù)列表) -> {執(zhí)行語句}

前面說了函數(shù)式接口是 lamdba 表達式的類型,所以其滿足以下要求:

  1. lamdba 表達式的參數(shù)列表對應函數(shù)式接口方法的參數(shù)列表

  2. lambda 的返回類型對應函數(shù)式接口方法的返回類型

例如 JDK 提供的 Predicate<T> 接口來判斷一個數(shù)是否大于 100:

Predicate<Integer> predicate = (Integer t) -> {return t > 100;};
System.out.println(predicate.test(300));//true

其中為了更加簡潔,以下情況可以省略一些代碼:

  1. 參數(shù)類型可省略
Predicate<Integer> predicate = (t) -> {return t > 100;};
  1. 入?yún)⒅挥幸粋€,括號可省略
Predicate<Integer> predicate = t -> {return t > 100;};
  1. 如果表達式只有一條語句,大括號和 'return' 都可以省略
Predicate<Integer> predicate = t -> t > 100;

說明:都沒有表明類型,程序是怎么知道的?其實是在編譯的時候,通過前面指定的泛型類型來推斷的,例如上面 Predicate<T> 中的 T 就是 Integer,進而推斷出 t 是 Integer 類型。####Lambda 另外一個表達方式

方法引用

? 如果 lambda 體中的內(nèi)容已經(jīng)有方法實現(xiàn)了,我們可以直接利用該方法代替。

語法如下:

使用規(guī)則:

  1. lamdba 體中調(diào)用方法的入?yún)⒘斜砗头祷刂殿愋?,要與函數(shù)接口抽象方法的一致!
  2. Lamdba 參數(shù)列表中的第一個參數(shù)是實例方法的調(diào)用者,而第二個參數(shù)是實例方法的參數(shù)時,可以使用類::實例方法名
  1. 對象:實例方法名
//1. 對象::實例方法名
@Test
public void methodRef() {
  Student student = new Student();
  //lambda 表達式方式
  Consumer<String> con = (x) -> student.setName(x);
  //方法引用方式
  Consumer<String> con2 = student::setName;
}
  1. 類:靜態(tài)方法名
//2. 類::靜態(tài)方法名
@Test
public void test2() {
  Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y);
  //方法引用方式
  Comparator<Integer> comparator2 = Integer::compare;
}
  1. 類:實例方法名
//3. 類::實例方法
//需要滿足:Lamdba 參數(shù)列表中的第一個參數(shù)是實例方法的調(diào)用者,而第二個參數(shù)是實例方法的參數(shù)時
public void test3() {
  BiPredicate<String, String> biPredicate = (x,y) -> x.equals(y);//x 為參數(shù)列表的第一個,并且是調(diào)用者,第二個參數(shù)y為方法的參數(shù)
  //方法引用方式
  BiPredicate<String, String> biPredicate2 = String::equals;
}
構造器引用
  1. 格式: Type::new
  2. 規(guī)則: 調(diào)用的構造器參數(shù)列表要與函數(shù)式接口抽象方法的參數(shù)列表保存一致

例如:

public void test4() {
  Supplier<Student> studentSupplier = () -> new Student();\
  //構造器引用:因為Supplier的抽象方法 get(),參數(shù)列表為空,所以調(diào)用Student的空參數(shù)構造器
  Supplier<Student> studentSupplier2 = Student::new;
}
數(shù)組引用

格式: Type[]::new


//數(shù)組引用
public void test5() {
  Function<Integer, String[]> function = (x) -> new String[x];
  //數(shù)組引用方式
  Function<Integer, String[]> function2 = String[]::new;
}

Stream

對比

舉個栗子:過濾string 集合中包含 "sam" 的元素

以前的寫法:

List<String> lists = new ArrayList<>();
List<String> result = new ArrayList<>();
for(String s : lists) {
  if (s.startsWith("sam")) {
    result.add(s);
  }
}

Stream 的寫法:是不是很爽?

List<String> lists = new ArrayList<>();
List<String> result = lists.stream().filter(s -> s.startsWith("sam")).collect(Collectors.toList());

Stream 兩種求值

一種是惰性求值,一種是及早求值。判斷是標準是:如果返回值是Stream,那么就是惰性求值,否則是及早求值。只有遇到及早求值才會執(zhí)行,否則不會執(zhí)行。

  1. 只有惰性求值不會執(zhí)行的:
List<Student> students = Arrays.asList(new Student("sam", 18, "男"));
Stream<String> stream = students.stream().map(student -> {
  System.out.println("name:" + student.getName());//沒有輸出
  return student.getName();
});
  1. 遇到及早求值 collect
    List<Student> students = Arrays.asList(new Student("sam", 18, "男"));
    long count = students.stream().map(student -> {
      System.out.println("name:" + student.getName());//輸出
      return student.getName();
    }).count();

Stream 常用操作

  1. map: 輸入 R 類型,返回 T 類型,用于類型轉(zhuǎn)換。屬于惰性求值
List<String> names = students.stream().map(student -> student.getName()).collect(Collectors.toList());
  1. collect: 從 stream 中生成一個集合,屬于及早求值。一般結(jié)合 Collectors.xxx,如上面例子的Collectors.toList(),它會根據(jù)返回值的類型自動生成對應的類型。如果需要指定具體類型,可以通過 toCollection ,它接受一個函數(shù)作為參數(shù),例如:
collect(Collectors.toCollection(TreeSet::new));//toCollection 參數(shù)是Supplier<C> 函數(shù)式接口\
  1. filter:過濾出滿足條件的數(shù)據(jù),也是 filter 表達式返回 true 的數(shù)據(jù)走到下一步操作。屬于惰性求值
List<Student> students = students.stream().filter(student -> student.getName().startsWith("sam")).collect(Collectors.toList());
  1. flatMap: 基于 map 的功能,并且將返回多個 stream 連接成一個 stream。屬于惰性求值
List<Student> students = Arrays.asList(new Student("sam", 18, "男"));
List<List<Student>> lists = Arrays.asList(students);
List<Student> allStudent = lists.stream().flatMap(students1 -> students1.stream()).collect(Collectors.toList());
//students1 -> students1.stream() : flatMap 將多個 stream 合成一個
  1. max 和 min: 求集合中最大值和最小值。屬于及早求值
Student student1 = new Student("sam", 18, "男");
Student student2 = new Student("lilian", 28, "女");
List<Student> students = Arrays.asList(student1, student2);
//求年齡最大的學生
Optional<Student> max = students.stream().max(Comparator.comparing(Student::getAge));
System.out.println(max.get());//lilian
  1. reduce: 將一組的值經(jīng)過操作(加減乘除等)生成一個值。reduce(a,b) 其中 a 表示上次的操作結(jié)果,b 表示下次操作的數(shù),例如:
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4);
//計算集合中數(shù)字總和
Integer total = integerStream.reduce(0,(a, b) -> a + b);//0 表示初始值,可選
System.out.println(total);//10
reduce.png

Optional

Optional 是用來替換 null 值的,用于鼓勵程序猿適當檢查變量是否為空,并提供更多簡便的操作 API.

常見用法
Optional<String> name = Optional.of("sam");
if (name.isPresent()) {//如果存在
  System.out.println(name.get());//取元素之前,如果不能確定是否一定有值,一定要判斷是否存在
} else {
  //orElse 用來設置默認值
  String d = name.orElse("default");
  //orElseGet 用于備選值計算太繁瑣而提供的
  String createDefault = name.orElseGet(() -> {
    return "sam";
  });
}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容