1. 程式人生 > 程式設計 >再來看看Java8的新特徵——lambda表示式

再來看看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、新的時間工具、預設方法等知識)。