繁体   English   中英

在Java中,为什么我不能使用lambda作为增强的for循环表达式?

[英]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的强制转换上下文中是兼容的。

  • 赋值上下文 - 赋值上下文允许将表达式的值(第15.26节)赋值给变量。
  • 调用上下文 - 调用上下文允许方法或构造函数调用中的参数值
  • 转换上下文 - 转换上下文允许转换运算符(第15.16节)的操作数转换为转换运算符显式命名的类型。

显然,这些都不是。 唯一可能的是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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM