一個新的JEP將用於增強lambda功能,提出的更改包括更好的消岐、對未使用的參數用下劃線標注和外部變量的跟蹤。雖然這些更改會使Java中的lambda表達式更類似於其它語言,但是初步討論表明大家都不同程度地支持這個方案。這個JEP補充了一系列其他建議來改進Java語言,包括局部變量類型推斷和增強的枚舉,所有這些改進都可能出現在Java 10中。
盡管上述三個更改都與lambda功能有關,但它們之間是相互獨立的,其中一些可能會被捨棄,而其他的則取決於反饋情況。因此,我們將在本文中單獨解釋它們。
更好的消歧
當在Java 8中添加lambda功能時,必須修改類型推斷以支持它們。然而,過去進行的更改並沒有達到預期的效果,部分原因是擔心這些更改會使第一次接觸lambda的開發者感到困惑。但是,從這一點來說,情況似乎正在改變,並且如果上下文提供了充足的信息,而編譯器無法推斷lambda的類型時,一些開發者會感到沮喪。以下例子說明了lambda類型推斷如何工作:
// Case 1: 推斷為Predicate <String>的lambda類型
// 第一個重載方法將被調用
private void m(Predicate<String> ps) { /* ... */ }
private void m(Function<String, String> fss) { /* ... */ }
private void callingM() {
m((String s) -> s.isEmpty());
}
// Case 2: 沒有足夠的信息來獨立地推斷lambda的類型
// 不過m2沒有被重載
// 將方法參數的簽名應用於lambda來推斷
// s是String類型並且s.length()返回Integer類型
private void m2(Function<String, Integer> fsi) { /* ... */ }
private void callingM2() {
m2(s -> s.length());
}
// Case 3: 沒有足夠的信息來獨立地推斷lambda的類型
// m3是重載的,但不同的版本有不同的參數數量
// 只有第一個版本和parameters (1)的數量匹配
// 將方法參數的簽名應用於lambda來推斷
// s是String類型並且s.length()返回Integer類型
private void m3(Function<String, Integer> fsi) { /* ... */ }
private void m3(Function<String, Integer> fsi, String s) { /* ... */ }
private void callingM3() {
m3(s -> s.length());
}
// Case 4: 沒有足夠的信息來獨立地推斷lambda的類型。
// m4的多個重載版本具有相同數量的可用參數
// Ambiguous call, ERROR
private void m4(Predicate<String> ps) { /* ... */ }
private void m4(Function<String, String> fss) { /* ... */ }
private void callingM4() {
m4(s -> s.isEmpty());
}
然而,對於最後一種情況來說,有足夠的信息來判斷m4的第一個重版本就是被調用的那個,盡管編譯器當下並沒有使用該信息。根據新建議,編譯器可以按照以下步驟來解決歧義:
類似的論證也可以應用於方法引用。
用下劃線表示未使用的參數
某些情況下,我們希望得到具有多個參數的lambda表達式,盡管不會使用所有的參數,但這要求開發者使用指示性名稱來表示未使用的參數。這個更改將允許使用下劃線來表示未使用的參數。
Function<String, Integer> noneByDefault = notUsed -> 0; // 目前
Function<String, Integer> noneByDefault = _ -> 0; // 建議
這是一個其他幾種語言都有的功能,如Scala、Ruby或Prolog,但是,在Java中這不能輕易地實現,因為直到Java 7,下劃線仍然是一個有效的標識符,它可以在代碼其它地方使用。為了引入這種更改而不需要重寫大量的代碼,這一功能需要逐步完善:
通過初步的討論來看,對這一更改的意見似乎並不一致;一些用戶喜歡新建議的簡潔性,但另一些用戶喜歡使用明確的名稱。在進一步的討論中可能達成共識。
參數的隱藏
這也許是新提出的功能中最有爭議的一個。當前lambda參數不允許影響外部變量,意味著參數名稱必須不同於當前作用域中可訪問的任何變量;這與其他包含作用域保持一致,比如while循環或if語句:
String s = "hello";
if(finished) {
String s = "bye"; // 錯誤,s已經定義
}
Predicate<String> ps = s -> s.isEmpty(); // 錯誤,s已經定義
如果建議的更改繼續下去,lambda參數將能夠重復使用和隱藏現有標識符。在某些情況下,這可能有利於用戶,也就是說他們不需要使用其他不太直接的名稱作為他們的lambda參數名稱(上面的例子通常會被重寫為s2 - > s2.isEmpty()),但是,就像國際知名演講者Roy Van Rijn提出的那樣,它也可能引入微小的錯誤:
Map<String, Integer> map = /* ... */
String key = "theInitialKey";
map.computeIfAbsent(key, _ -> {
String key = "theShadowKey"; // shadow variable
return key.length();
});
目前,上述代碼是不被允許的,但根據新提案這也可能是對的。如果標記為“shadow variable”的行被刪除,代碼將仍然可以編譯和運行,但它會做完全不同的事情。
為了評估上述更改是否將被引入Java以及將以什麼形式引入,他們還將進行更多的討論。然而,毫無疑問的是,在Java 8中引入lambda只是未來Java語言的眾多改進的第一步。
查看英文原文:Java 10 Could Bring Upgraded Lambdas