歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java 10將帶來升級版的Lambda

Java 10將帶來升級版的Lambda

日期:2017/3/1 9:06:16   编辑:Linux編程

一個新的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的第一個重版本就是被調用的那個,盡管編譯器當下並沒有使用該信息。根據新建議,編譯器可以按照以下步驟來解決歧義:

  1. 這兩種可能性都假定lambda的參數是一個String,所以s可以被認為是String類型
  2. 現在我們知道s是一個String,我們知道方法String.isEmpty()返回boolean
  3. 由於lambda返回booleanm4的第二個變體不匹配,因此它被捨棄
  4. 只剩下一個選項就是m4的第一個變體,它與lambda的推斷類型匹配,因此使用第一個變體

類似的論證也可以應用於方法引用。

用下劃線表示未使用的參數

某些情況下,我們希望得到具有多個參數的lambda表達式,盡管不會使用所有的參數,但這要求開發者使用指示性名稱來表示未使用的參數。這個更改將允許使用下劃線來表示未使用的參數。

Function<String, Integer> noneByDefault = notUsed -> 0; // 目前
Function<String, Integer> noneByDefault = _ -> 0; // 建議

這是一個其他幾種語言都有的功能,如Scala、Ruby或Prolog,但是,在Java中這不能輕易地實現,因為直到Java 7,下劃線仍然是一個有效的標識符,它可以在代碼其它地方使用。為了引入這種更改而不需要重寫大量的代碼,這一功能需要逐步完善:

  1. Java 8:當下劃線用作標識符時,會發出警告,阻止開發人員使用它;下劃線不允許作為lambda中的標識符(這不會導致任何向後不兼容問題,因為在Java 8之前的版本並沒有lambda功能)。
  2. Java 9:Java 8中的警告變為錯誤,確保將使用下劃線作為標識符從Java代碼語料庫中刪除。
  3. Java 10(或更高版本):下劃線作為標識符重新引入,但僅適用於lambda表達式中未使用的參數。

通過初步的討論來看,對這一更改的意見似乎並不一致;一些用戶喜歡新建議的簡潔性,但另一些用戶喜歡使用明確的名稱。在進一步的討論中可能達成共識。

參數的隱藏

這也許是新提出的功能中最有爭議的一個。當前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

Copyright © Linux教程網 All Rights Reserved