lambda 表达式除了保存代码行之外还有其他用途吗?

2022-08-31 08:43:48

lambda 表达式除了保存代码行之外还有其他用途吗?

lambdas是否提供了任何特殊功能来解决不容易解决的问题?我看到的典型用法是,而不是写这个:

Comparator<Developer> byName = new Comparator<Developer>() {
  @Override
  public int compare(Developer o1, Developer o2) {
    return o1.getName().compareTo(o2.getName());
  }
};

我们可以使用 lambda 表达式来缩短代码:

Comparator<Developer> byName =
(Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName());

答案 1

Lambda 表达式通常不会改变您通常可以使用 Java 解决的问题集,但肯定会使解决某些问题变得更加容易,原因与此相同,我们不再使用汇编语言进行编程。从程序员的工作中删除多余的任务使生活更轻松,并允许做一些你甚至不会碰到的事情,只是为了你必须(手动)产生的代码量。

但 lambda 表达式不仅仅是在保存代码行。Lambda 表达式允许您定义函数,在此之前,您可以使用匿名内部类作为解决方法,这就是为什么在这些情况下您可以替换匿名内部类的原因,但通常不能。

最值得注意的是,lambda表达式是独立于它们将转换为的功能接口定义的,因此它们无法访问任何继承成员,此外,它们无法访问实现功能接口的类型实例。在 lambda 表达式中,并且与在周围的上下文中具有相同的含义,另请参阅此答案。此外,不能创建新的局部变量来隐藏周围上下文的局部变量。对于定义函数的预期任务,这消除了许多错误源,但这也意味着对于其他用例,可能存在无法转换为lambda表达式的匿名内部类,即使实现功能接口也是如此。thissuper

此外,该构造保证生成一个新的不同实例(与往常一样)。匿名内部类实例始终保留在非上下文¹ 中创建的对其外部实例的引用。相反,lambda 表达式仅在需要时捕获对的引用,即如果它们访问或非成员。它们会生成有意未指定身份的实例,这允许实现在运行时决定是否重用现有实例(另请参阅“lambda 表达式是否在每次执行时都会在堆上创建对象?”)。new Type() { … }newstaticthisthisstatic

这些差异适用于您的示例。您的匿名内部类构造将始终生成一个新实例,它也可能捕获对外部实例的引用,而您的是一个非捕获 lambda 表达式,在典型实现中计算结果为单一实例。此外,它不会在硬盘驱动器上生成文件。(Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName()).class

考虑到语义和性能方面的差异,lambda表达式可能会改变程序员将来解决某些问题的方式,当然,这也是由于新的API包含了利用新语言功能进行函数式编程的想法。另请参阅 Java 8 lambda 表达式和一等值


¹ 从 JDK 1.1 到 JDK 17。从 JDK 18 开始,如果不使用外部实例,则内部类可能不会保留对外部实例的引用。出于兼容性原因,这要求内部类不可序列化。这仅适用于在 JDK 18 或更高版本下(重新)编译具有目标 JDK 18 或更高版本的内部类的情况。另请参阅 JDK-8271717


答案 2

编程语言不是供机器执行的。

它们是程序员思考的

语言是与编译器的对话,将我们的想法变成机器可以执行的东西。从其他语言(或将其留给其他语言)的人对Java的主要抱怨之一曾经是它强加给程序员某种心理模型(即一切都是一个类)。

我不打算权衡这是好是坏:一切都是权衡取舍。但是Java 8 lambdas允许程序员从函数的角度思考,这是你以前在Java中无法做到的。

这与程序程序员在进入Java时学会从类的角度思考是一回事:你会看到它们逐渐从美化结构的类中移动,并拥有具有一堆静态方法的“帮助器”类,然后转向更接近理性OO设计的东西(mea culpa)。

如果你只是把它们看作是一种表达匿名内部类的简短方式,那么你可能不会发现它们非常令人印象深刻,就像上面的程序程序员可能不认为类有任何很大的改进一样。


推荐