再來看看Java8的新特徵——lambda表示式
什麼是lambda表示式?
可以把Lambda表示式理解為簡潔地表示可傳遞的匿名函式的一種方式:它沒有名稱,但它有引數列表、函式主體、返回型別,可能還有一個可以丟擲的異常列表。
比如說new一個Thread的傳統寫法如下
Thread t = new Thread(new Runnable() {
public void run(){
System.out.println("Hello world");
}
});
複製程式碼
那麼利用lambda表示式的寫法就是
Thread t = new Thread(() -> System.out.println("Hello world" ));
複製程式碼
->
左邊的就是引數列表,->
右邊的就是函式主體
函式式介面
為什麼@FunctionalInterface註解修飾的類只能有一個抽象函式
檢視Java8的原始碼,被@FunctionalInterface修飾的函式叫做函式式介面,例如Predicate
,這些類往往只有一個抽象函式,那是因為“Lambda表示式理解為簡潔地表示可傳遞的匿名函式
”,直接使用的匿名函式的時候沒有指定函式名稱,所以,如果有兩個及以上抽象函式的時候,虛擬機器器就不知道你要執行哪個方法了,如上例中Runnable的run()方法,我們引數列表部分只使用了(),並沒有宣告呼叫的函式名。
JDK自帶的函式式介面都在java.util.function路徑下,常用的有
public interface Predicate<T>{
boolean test (T t);
}
public interface Consumer<T> {
void accept(T t);
}
public interface Function<T,R> {
R apply(T t);
}
...
複製程式碼
函式式介面使用示例
//原始碼部分
@FunctionalInterface
public interface Predicate<T>{
boolean test(T t);
}
//方法構建
public static <T> List<T> filter(List<T> list,Predicate<T> p) {
List<T> results = new ArrayList<>();
for (T s: list){
if(p.test(s)){
results.add(s);
}
}
return results;
}
//使用示例,通過filter方法,篩選出String不為空的資料
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings,nonEmptyStringPredicate);
複製程式碼
其他函式式介面使用示例
為什麼lambda表示式使用區域性變數必須是final的?
lambda表示式主體部分除了使用引數列表的資料,還可以使用lambda表示式外部的區域性變數,但是這些區域性變數只能宣告一次,否則就會報錯。
int portNumber = 1337;
//此時會報錯,portNumber必須被final修飾
Runnable r = () -> System.out.println(portNumber);
portNumber = 31337;
複製程式碼
因為lambda表示式主體可看作是匿名內部類,訪問外部區域性變數是需要final的。從執行緒的角度來說,就是區域性變數是一個執行緒(假設叫執行緒A),lambda表示式主體是另外一個執行緒(執行緒B),當執行緒A結束的時候,執行緒B還要訪問執行緒A的資料,肯定是不行的,所以執行緒B中的變數實質上不是指向執行緒A中的變數,而是拷貝了一份出來,所以必須保證拷貝出來的資料是不可以改變的。
方法引用
lambda表示式還有一個非常方便的地方,就是方法引用,可以通過類名::方法名
的形式直接使用方法。
例如
//靜態方法
Integer::parseInt
//物件的普通方法
String::length
//構造方法
Apple::new
複製程式碼
複合lambda表示式的用法
lambda表示式還可以鏈式呼叫,同時擁有與或非(negate、and和or)的邏輯判斷
//鏈式呼叫
inventory.sort(comparing(Apple::getWeight)
.reversed()
.thenComparing(Apple::getCountry));
//非
Predicate<Apple> notRedApple = redApple.negate();
//與
Predicate<Apple> redAndHeavyApple =
redApple.and(a -> a.getWeight() > 150);
//或
Predicate<Apple> redAndHeavyAppleOrGreen =
redApple.and(a -> a.getWeight() > 150)
.or(a -> "green".equals(a.getColor()));
複製程式碼
函式複合
Function函式介面提供了兩個方法對資料做連續操作
,andThen和compose方法。
Function<Integer,Integer> f = x -> x + 1;
Function<Integer,Integer> g = x -> x * 2;
Function<Integer,Integer> h = f.andThen(g);
int result = h.apply(1);
//輸出3 ==> )(1*2)+1
複製程式碼
andThen方法相當於先執行f函式,再執行g函式。
Function<Integer,Integer> h = f.compose(g);
int result = h.apply(1);
//輸出4 ==> (1+1)*2
複製程式碼
compose方法相當於先執行g函式,再執行f函式。
接下來
接下來會梳理流的相關知識點、和其他(注入Optionnal、新的時間工具、預設方法等知識)。