java8 的接口新特性
先看看下面的接口的默認方法和靜態(tài)方法,感受一下:
public interface Java8Interface {
//默認方法
default String createName() {
//邏輯實現(xiàn)
return "xxxx";
}
//靜態(tài)方法
public static LocalDateTime getLocalDate() {
return LocalDateTime.now();
}
}
- 默認實現(xiàn)方法
如果多繼承狀態(tài)下有默認方法,遵循 3 條規(guī)則
類勝與接口,如果在繼承鏈中有方法體或抽象的方法聲明,可以忽略接口中的定義
子類勝于父類。如果一個接口繼承了另外一個接口,而且兩個接口都定義了相同的默認方法,那么子類中的默認方法勝出
如上都不適用,要么子類實現(xiàn)該方法,要么將方法聲明為抽象方法
- 靜態(tài)方法
為了幫助編寫類庫的開發(fā)人員,Java 8 增加類接口靜態(tài)方法新特性
lambda 表達式
函數(shù)式接口
函數(shù)式接口是 lambda 表達式的類型,其特點是只有一個抽象方法的接口,但是可以有 default 類型的方法實現(xiàn)。
JDK 提供幾個常用的函數(shù)式接口
- Predicate<T>:通過傳入一個對象T,返回是否匹配
@FunctionalInterface //這個注解是作用是為了標識該接口是函數(shù)式接口,編譯時可以檢查語法是否準確
public interface Predicate<T> {
//唯一一個抽象方法
boolean test(T t);
//其他 default 的方法實現(xiàn)省略。。。
}
- Consumer<T>:傳入對象 T,沒有返回值,是一種消費型接口
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
- Function<T,R> :一個入?yún),返回值 R
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
- Supplier<T> : 沒入?yún)?,返?T
@FunctionalInterface
public interface Supplier<T> {
T get();
}
lambda 表達式語法
(參數(shù)列表) -> {執(zhí)行語句}
前面說了函數(shù)式接口是 lamdba 表達式的類型,所以其滿足以下要求:
lamdba 表達式的參數(shù)列表對應函數(shù)式接口方法的參數(shù)列表
lambda 的返回類型對應函數(shù)式接口方法的返回類型
例如 JDK 提供的 Predicate<T> 接口來判斷一個數(shù)是否大于 100:
Predicate<Integer> predicate = (Integer t) -> {return t > 100;};
System.out.println(predicate.test(300));//true
其中為了更加簡潔,以下情況可以省略一些代碼:
- 參數(shù)類型可省略
Predicate<Integer> predicate = (t) -> {return t > 100;};
- 入?yún)⒅挥幸粋€,括號可省略
Predicate<Integer> predicate = t -> {return t > 100;};
- 如果表達式只有一條語句,大括號和 'return' 都可以省略
Predicate<Integer> predicate = t -> t > 100;
說明:都沒有表明類型,程序是怎么知道的?其實是在編譯的時候,通過前面指定的泛型類型來推斷的,例如上面 Predicate<T> 中的 T 就是 Integer,進而推斷出 t 是 Integer 類型。####Lambda 另外一個表達方式
方法引用
? 如果 lambda 體中的內(nèi)容已經(jīng)有方法實現(xiàn)了,我們可以直接利用該方法代替。
語法如下:
使用規(guī)則:
- lamdba 體中調(diào)用方法的入?yún)⒘斜砗头祷刂殿愋?,要與函數(shù)接口抽象方法的一致!
- Lamdba 參數(shù)列表中的第一個參數(shù)是實例方法的調(diào)用者,而第二個參數(shù)是實例方法的參數(shù)時,可以使用類::實例方法名
- 對象:實例方法名
//1. 對象::實例方法名
@Test
public void methodRef() {
Student student = new Student();
//lambda 表達式方式
Consumer<String> con = (x) -> student.setName(x);
//方法引用方式
Consumer<String> con2 = student::setName;
}
- 類:靜態(tài)方法名
//2. 類::靜態(tài)方法名
@Test
public void test2() {
Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y);
//方法引用方式
Comparator<Integer> comparator2 = Integer::compare;
}
- 類:實例方法名
//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;
}
構造器引用
- 格式: Type::new
- 規(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í)行。
- 只有惰性求值不會執(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();
});
- 遇到及早求值 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 常用操作
- map: 輸入 R 類型,返回 T 類型,用于類型轉(zhuǎn)換。屬于惰性求值
List<String> names = students.stream().map(student -> student.getName()).collect(Collectors.toList());
- collect: 從 stream 中生成一個集合,屬于及早求值。一般結(jié)合 Collectors.xxx,如上面例子的Collectors.toList(),它會根據(jù)返回值的類型自動生成對應的類型。如果需要指定具體類型,可以通過 toCollection ,它接受一個函數(shù)作為參數(shù),例如:
collect(Collectors.toCollection(TreeSet::new));//toCollection 參數(shù)是Supplier<C> 函數(shù)式接口\
- filter:過濾出滿足條件的數(shù)據(jù),也是 filter 表達式返回 true 的數(shù)據(jù)走到下一步操作。屬于惰性求值
List<Student> students = students.stream().filter(student -> student.getName().startsWith("sam")).collect(Collectors.toList());
- 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 合成一個
- 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
- 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

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";
});
}