Java Lambda 和闭包

Posted

技术标签:

【中文标题】Java Lambda 和闭包【英文标题】:Java Lambdas and Closures 【发布时间】:2012-07-09 09:25:20 【问题描述】:

我听说 lambda 即将在您附近的 Java (J8) 中出现。我在一些博客上找到了它们的外观示例:

SoccerService soccerService = (teamA, teamB) -> 
    SoccerResult result = null;
    if (teamA == teamB) 
        result = SoccerResult.DRAW;
    
    else if(teamA < teamB) 
        result = SoccerResult.LOST;
    
    else 
        result = SoccerResult.WON;
    

    return result;
;

所以马上开始:

teamAteamB 在哪里输入?或者它们不是(就像某种奇怪形式的泛型)? lambda 是闭包的类型,还是相反? 与典型的匿名函数相比,这会给我带来什么好处?

【问题讨论】:

见what-is-the-difference-between-a-closure-and-a-lambda 【参考方案1】:

Lambda 表达式只是实现目标接口的语法糖,这意味着您将通过 lambda 表达式在接口中实现特定方法。编译器可以推断接口中参数的类型,这就是为什么您不需要在 lambda 表达式中显式定义它们的原因。

例如:

Comparator<String> c = (s1, s2) -> s1.compareToIgnoreCase(s2);

在这个表达式中,lambda 表达式显然实现了一个字符串Comparator,因此,这意味着 lambda 表达式是实现compare(String, String) 的语法糖。

因此,编译器可以安全地假定s1s2 的类型是String

您的目标接口类型提供了编译器确定 lambda 参数的实际类型所需的所有信息。

Oracle Corportion 的 Java 语言架构师 Briant Goetz 发表了几篇关于 JDK 8 Lambdas 正在进行的工作的文章。我相信您的问题的答案就在那里:

State of Lambda。 State of Lambda Libraries Edition。 Translation of Lambda Expressions JVMLS 2012: Implementing Lambda Expressions in Java

第二篇文章解释了 lambda 表达式是如何在字节码级别实现的,可能会帮助您深入研究第二个问题的细节。

【讨论】:

“Lambda 表达式只是实现目标接口的语法糖”——不,它不是。在这种情况下,它们必然是内部类的语法糖。 Java 语言的作者明确指出,lambdas 只是内部类的语法糖! Lambda 是一流的构造。 -- oracle.com/events/us/en/java8/index.html 7:30min @AlexanderOrlov 你误读了我的话。首先,syntactic sugar 的意思是“一种使事情更容易阅读的替代语法”。而且我从未说过它们是内部类的语法糖,我说的是“实现接口的语法糖”,据我所知,这是准确的,因为评估 lambda 表达式的结果是目标接口的综合实现。正是出于这个原因,我故意选择了我的话。 但是将实现的接口(=接口实现)的结果作为参数传递给方法与传递实现该接口的内部类不是一回事吗? @AlexanderOrlov 您的问题的答案只是证明了在 Java 中实现接口的方法不止一种(即通过匿名类和现在通过 lambda 表达式声明一个实现它的命名类) .【参考方案2】:

有关该示例的完整版本,请参阅 this page(但是,相关部分如下所示)。

类型是从 SoccerService 接口和 SoccerResult 枚举中推断出来的,没有显示在你的 sn-p 中:

enum SoccerResult
    WON, LOST, DRAW


interface SoccerService 
    SoccerResult getSoccerResult(Integer teamA, Integer teamB);

(lambdas 与标准匿名相比)的好处只是减少了冗长:

(x, y) => x + y

相对于:

new Adder()

  public int add(int x, int y)
  
    return x + y;
  

关于闭包和 lambda 之间的区别,请参阅this question。

【讨论】:

“此页面”链接断开。 @powder366 现在已修复,谢谢(示例的相关部分已复制到我的答案中,因此您并没有错过太多!)【参考方案3】: teamA 和 teamB 在哪里输入?或者它们不是(就像某种奇怪形式的泛型)?

Lambda 使用目标类型,很像通用方法调用(从 1.5 开始)和菱形 [not an] 运算符(从 1.7 开始)。粗略地讲,结果应用到的类型被声明(或可以推断),用于提供单一抽象方法 (SAM) 基本类型的类型,从而提供方法参数类型。

作为 1.5 中泛型方法推断的示例:

Set<Team> noTeams = Collections.emptySet(); 

1.7 中的菱形运算符:

Set<Team> aTeams = new HashSet<>();

团队,团队,团队,团队,团队,团队。我什至喜欢说团队这个词。

lambda 是一种闭包,还是相反?

lambda 是一种有限形式的闭包,其方式与匿名内部类几乎完全相同,但有一些随机差异可以让您发现:

外部this 不会被内部this 隐藏。这意味着 lambda 和匿名内部类中的相同文本可能意味着微妙但完全不同的东西。这应该会让 Stack Overflow 忙于处理奇怪的问题。

为了弥补内部this 的不足,如果直接分配给局部变量,那么该值可以在 lambda 中访问。 IIRC(我可以检查,但不会),在匿名内部类中,本地将在范围内并在外部范围内隐藏变量,但您不能使用它。我相信缺少实例初始化器使得这更容易指定。

没有被标记为final 但可能被标记的本地字段被视为final。所以它们不在范围之内,但您实际上可以读取(尽管不能写入)它们。

与典型的匿名函数相比,这会给我带来什么好处?

更简洁的语法。就是这样。

当然,Java 语法的其余部分和以往一样糟糕透顶。

我不相信这是在初始实现中,但不是作为 [内部] 类实现,而是 lambdas 可以使用方法句柄。方法句柄的性能与之前的预测相比有所下降。取消类,应该减少字节码占用,可能是运行时占用,当然还有类加载时间。可能有一个实现,其中大多数匿名内部类(不是Serializable,普通的静态初始化器)没有通过构思不佳的类加载机制,没有任何特别明显的不兼容性。

(希望我的术语隐藏正确。)

【讨论】:

当您尝试修改非最终变量时会引发什么编译器错误(如果我理解您的答案正确)?

以上是关于Java Lambda 和闭包的主要内容,如果未能解决你的问题,请参考以下文章

Java中Lambda表达式和Groovy闭包的相关解析

从 λ 演算看 JS 与 JAVA8 闭包

lambda表达式和闭包

C# 闭包中的 Lambda 表达式是啥?

C# 闭包中的 Lambda 表达式是啥?

Rust 闭包和 Haskell lambda 有啥区别? [关闭]