[英]In Java, why can't I use a lambda as an enhanced for loop's Expression?
假設我們有一個Iterator<Integer> iterator
。 因為Iterable
是一個功能界面,我們可以寫:
Iterable<Integer> iterable = () -> iterator;
我們當然可以使用iterable
作為增強的for循環表達式 :
for (Integer i : iterable) foo(i);
那么為什么呢
for (Integer i : () -> iterator) foo(i);
不允許? (導致以下編譯器錯誤:)
error: lambda expression not expected here
for (Integer i : () -> iterator) foo(i);
^
使目標類型顯式為
for (Integer i : (Iterable<Integer>) () -> iterator) foo(i);
顯然有效,但為什么編譯器不能推斷出λ-expression的目標類型? 從表達式是λ表示法的事實來看,編譯器是否應該清楚目標類型不能是一個Array
,因此必須是Iterable
?
這只是語言設計師的疏忽,還是我還缺少其他的東西?
這不僅僅是關於lambda表達式; 它是關於所有需要目標類型的多重表達式。
有一點可以肯定的是,這不是一種疏忽; 案件被審議並被駁回。
引用早期規范:
http://cr.openjdk.java.net/~dlsmith/jsr335-0.9.3/D.html
決定允許哪些上下文支持多表達式在很大程度上取決於對這些功能的實際需求:
增強的for循環中的表達式不在多邊形上下文中,因為,當構造當前被定義時,就好像表達式是接收器:
exp.iterator()
(或者,在數組的情況下,exp[i]
) 。 可以通過lambda表達式(for (String s : () -> stringIterator)
)將Iterator作為Iterable包裝在for循環中,但這與Iterable的語義不能很好地for (String s : () -> stringIterator)
。
我的看法是,每次調用Iterable.iterator()
必須返回一個新的,獨立的迭代器,位於開頭。 但是,示例(以及您的示例中)中的lambda表達式返回相同的迭代器。 這不符合Iterable
的語義。
在任何情況下,支持for-each循環中的目標類型似乎是不必要的工作。 如果您已經擁有迭代器,那么您可以這樣做
iterator.forEachRemaining( element->{ ... } )
或者如果你喜歡老派
while(iterator.hasNext()) {
Foo elment = iterator.next();
也不是太糟糕; 它不值得使語言規范復雜化。 (如果我們確實希望每個人都提供目標類型,請記住它也需要適用於其他多邊形表達式,例如?:
;然后for-each在某些情況下可能變得難以理解。通常,有兩個可能的目標類型, Iterable<? extends X> | X[]
,這對於類型推理系統來說非常困難。)
for-each構造可以被認為是語法糖,因為lambda不可用。 如果語言已經有lambda表達式,那么實際上沒有必要使用特殊的語言結構來支持每個語言。 它可以通過庫API完成。
在Lambda表達式文檔中 ,它們列出了可以推斷目標類型的方案,具體為:
要確定lambda表達式的類型,Java編譯器將使用發現lambda表達式的上下文或情境的目標類型。 因此,您只能在Java編譯器可以確定目標類型的情況下使用lambda表達式:
變量聲明
分配
退貨聲明
數組初始化器
方法或構造函數參數
Lambda表達體
條件表達式,?:
轉換表達式
此方案不是這些方案,因此無法推斷目標類型。 我同意你的意見,目標類型永遠不能是一個數組(因為數組不是一個功能接口),但文檔很清楚,這不是這些場景之一。
具體來說,在JLS 15.27.3中 ,它說:
如果T是函數接口類型(第9.8節),並且表達式與從T派生的地面目標類型的函數類型一致,則lambda表達式在賦值上下文,調用上下文或具有目標類型T的強制轉換上下文中是兼容的。
顯然,這些都不是。 唯一可能的是Invocation上下文,但增強的for
循環結構不是方法調用。
至於“為什么”Java作者不允許這種情況,我不知道。 說到Java編寫者的想法通常超出了Stack Overflow的范圍; 我試圖解釋為什么代碼不起作用,但我無法猜測為什么他們選擇以這種方式編寫代碼。
附錄,@ bayou.io的答案中的解釋/討論仍然存在於JSR-335的最終版本中 :
Lambda表達式和方法引用可能只出現在某些上下文中,它們的類型和正確性由此上下文決定。 現有語言中的其他類型的表達式已經引入了對上下文的依賴性,這種趨勢似乎可能會持續下去。 而不是以特別方式處理每個新特征,聚合表達的引入和目標類型可以影響表達類型的顯式識別允許我們統一處理單個傘下的依賴於上下文的表達。
......剪斷
增強的for循環中的表達式不在多邊形上下文中,因為,當構造當前被定義時,就好像表達式是接收器:
exp.iterator()
(或者,在數組的情況下,exp[i]
) 。 可以通過lambda表達式(for (String s : () -> stringIterator)
)將Iterator作為Iterable包裝在for循環中,但這與Iterable的語義不能很好地for (String s : () -> stringIterator)
。
正如durron597解釋的那樣 ,只有在Java編譯器可以確定目標類型的情況下才能使用lambda表達式。
在for-each循環中:
for(FormalParameter:Expression)Statement
JLS說 :
Expression的類型必須是Iterable或數組類型(第10.1節),否則會發生編譯時錯誤。
這意味着允許兩種不同的類型,而不是lambda表達式所需的類型,因此無法以lambda表達式為每個循環工作的方式設計語言。 沒有使用lambda表達式的概念。
添加強制轉換時,只允許一種類型,因此編譯器可以確定lambda表達式的目標類型。 這就是“使目標類型顯式化”的原因。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.