Java最佳实践中是否会尽可能使用Lambda表达式?


52

我最近掌握了Java 8中引入的Lambda表达式。我发现,每当我使用功能接口时,我总是总是使用Lambda表达式,而不是创建实现该功能接口的类。

这被认为是好的做法吗?还是他们的情况不适合将Lambda用于功能接口?


122
对于“是否在可能的最佳实践中使用X技术”这个问题 ,唯一正确的答案是“不,在可行的情况下不使用X技术”
布朗

选择何时使用lambda(匿名)与其他某种功能实现(例如方法引用)的选择是一个非常有趣的问题。几天前,我有机会见到了乔什·布洛赫(Josh Bloch),这是他谈到的重点之一。从他所说的内容来看,我知道他正计划在下一版的有效Java中添加一个新项目来处理这个确切的问题。
丹尼尔·普里登

@DocBrown那应该是答案,而不是评论。
gnasher729

1
@ gnasher729:绝对不是。
布朗

Answers:


116

有许多条件应使您考虑不使用lambda:

  • 大小 lambda越大,遵循周围逻辑的难度就越大。
  • 重复最好为重复的逻辑创建一个命名函数,尽管可以重复分散的非常简单的lambda。
  • 命名如果可以想到一个好的语义名称,则应改用该名称,因为它可以使代码更加清晰。我不是在说像这样的名字priceIsOver100x -> x.price > 100和这个名字一样清楚。我的意思是这样的名字isEligibleVoter代替了一长串条件。
  • 嵌套嵌套lambda确实非常难以阅读。

不要太过分。请记住,软件很容易更改。如有疑问,请以两种方式编写它,然后看哪个更容易阅读。


1
考虑通过lambda处理的数据量也很重要,因为lambda在处理少量数据时效率很低。它们在大型数据集的并行化方面确实很出色。
CraigR8806

1
@ CraigR8806我认为这里的性能不是问题。使用匿名函数或创建扩展函数接口的类应具有相同的性能。当您谈论在命令式样式循环中使用streams / higher-order-functions api时,会考虑性能问题,但是在这个问题中,我们在两种情况下都使用功能接口,只是语法不同。
puhlen

17
“尽管可以重复散布的非常简单的lambda,这是可以的”,但前提是它们不必一起改变。如果它们必须都是相同的逻辑以使代码正确,则应使它们实际上全部相同,从而只需要在一个位置进行更改即可。
jpmc26

总的来说,这是一个很好的答案。提到将方法引用用作lambda的替代方法,将只能解决我在此答案中看到的漏洞。
Morgen

3
如有疑问,请以两种方式编写,并请其他人维护易于阅读的代码。
corsiKa

14

这取决于。每当发现自己在不同位置使用相同的lambda时,都应考虑实现一个实现该接口的类。但是,如果您将使用匿名内部类,则我认为lambda更好。


6
不要忘记使用方法引用的可能性!正是由于这个原因,Oracle到处都添加了(并正在Java 9中添加更多)静态方法和默认方法。
约尔格W¯¯米塔格

13

我支持Karl Bielefeldt的回答,但想提供一个简短的补充。

  • 调试 某些IDE与lambda内的作用域作斗争,并努力在lambda的上下文内显示成员变量。希望这种情况会有所改变,但是当杂乱无章地保存别人的代码时,可能会很烦人。

绝对适用于.NET。不一定会让我避免使用lambda,但是如果我做其他事情,我当然不会感到内。
user1172763

3
如果是这种情况,则说明您使用了错误的IDE。IntelliJ和Netbeans在该特定领域都做得很好。
David Foerster

1
@ user1172763在Visual Studio中,如果要查看成员变量,我发现您可以向上调用堆栈,直到到达lambda的上下文为止。
Zev Spitz

6

访问封闭范围的局部变量

Karl Bielefeldt接受的答案是正确的。我可以再加上一个区别:

  • 范围

嵌套在类内方法内的lambda代码可以访问在该方法和类内找到的任何有效最终变量。

创建实现该功能接口的类不会使您直接访问调用代码的状态。

引用Java教程(重点煤矿):

像本地和匿名类一样,lambda表达式可以捕获变量。它们对封闭范围的局部变量具有相同的访问权。但是,与本地和匿名类不同,lambda表达式没有任何阴影问题(有关更多信息,请参见阴影)。Lambda表达式具有词法范围。这意味着它们不会从超类型继承任何名称,也不会引入新的作用域级别。解释lambda表达式中的声明就像在封闭环境中一样。

因此,虽然提取长代码并命名有很多好处,但您必须权衡直接访问封闭方法和类的状态的简单性。

看到:


4

这可能是挑剔的,但是除了其他答案中提到的所有其他优点之外,我还要添加:

尽可能使用方法参考。相比:

employees.stream()
         .map(Employee::getName)
         .forEach(System.out::println);

employees.stream()
         .map(employee -> employee.getName())
         .forEach(employeeName -> System.out.println(employeeName));

使用方法引用可以省去命名lambda参数的麻烦,这通常是多余的,并且/或者导致诸如e或的惰性名称x


0

在Matt McHenry的答案的基础上,lambda和方法引用之间可能存在某种关系,其中lambda可以重写为一系列方法引用。例如:

employees.stream()
         .forEach(employeeName -> System.out.println(employee.getName()));

employees.stream()
         .map(Employee::getName)
         .forEach(System.out::println);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.