Lambda表达式与方法引用

Ger*_*ard 106 java intellij-idea java-8

IntelliJ一直建议我用方法引用替换我的lambda表达式.

两者之间是否存在客观差异?

Bri*_*etz 163

让我提供一些观点,说明为什么我们将这个特性添加到语言中,显然我们并不是非常需要(所有方法引用都可以表示为lambda).

请注意,没有正确的答案.任何人说"总是使用方法ref而不是lambda"或"总是使用lambda而不是方法ref"应该被忽略.

这个问题在精神上与"何时应该使用命名类与匿名类"非常相似?答案是一样的:当你发现它更具可读性时.当然有一些肯定是一个或绝对是另一个但是中间有一大堆灰色,必须使用判断.

方法refs背后的理论很简单:名称很重要.如果一个方法有一个名称,那么通过名称引用它,而不是通过最终只是转身并调用它的命令包代码,通常(但不总是!)更清晰和可读.

关于表现或关于计算字符的论点大多是红色的鲱鱼,你应该忽略它们.目标是编写清晰明确的代码.很多时候(但并不总是!)方法引用此指标的胜利,因此我们将它们作为选项包含在这些情况下使用.

关于方法引用是否澄清或混淆意图的关键考虑因素是从上下文中是否明显表示所表示的函数的形状.在某些情况下(例如,map(Person::getLastName)从上下文中可以清楚地看到需要将一个函数映射到另一个函数的函数,并且在这种情况下,方法引用会发光.在其他情况下,使用方法ref需要读者想知道什么样的正在描述函数;这是一个警告标志,即lambda可能更具可读性,即使它更长.

最后,我们发现大多数人首先避开方法引用,因为他们觉得比lambdas更新,更怪,因此最初发现它们"不太可读",但随着时间的推移,当他们习惯了语法时,通常会改变他们的行为并尽可能地倾向于方法参考.所以请注意,你自己的主观初始"不太可读"的反应几乎肯定会带来一些熟悉偏见的方面,你应该给自己一个机会在提出风格意见之前对两者都感到满意.

  • @Gerard谢谢你会是一个更好的答案. (23认同)
  • @Gerard听起来Brian说"不,没有" (3认同)
  • 只是想对这个出色的答案表示感谢。最后一段特别引起了我的共鸣,并使我更加接受方法引用。 (2认同)

ovu*_*tin 10

由多个语句组成的长lambda表达式可能会降低代码的可读性.在这种情况下,在方法中提取这些语句并引用它可能是更好的选择.

另一个原因可能是可重用性.您可以构造一个方法并从代码的不同位置调用它,而不是复制和粘贴几个语句的lambda表达式.

  • 我和杰拉德在一起.当您移出代码时,可读性实际上会受到影响,因此您必须跳转到它以继续读取,然后跳回去.您*希望*将所有相关代码放在同一个地方.此外,`House`是一个非常良性的例子; 怎么样`ThreeStoryRedBrickHouseWithBlueDoors`.我更喜欢多参数lambda的方法引用,有时强调lambda只是一个方法调用.方法引用不会出错:您可能会在使用站点拼错参数,意外地引用外部作用域中的变量等. (9认同)
  • 比较:`houses.map(House :: getName)`和`houses.map(h - > h.getName())`.lambda少占两个字符.确实,类型不是明确的,但任何IDE都会告诉你,此外,当类型明显时,应该使用lambdas.我可能会同意你的可重用性,但lambda只是很小,所以它们可以被链接而不是创建大的特定方法.从这个意义上说,小方法比一些大而复杂的方法更可重用,并且由于lambda的清晰度(在某种程度上,冗长),它们仍然易于阅读. (2认同)

aer*_*ode 10

正如用户stuchl4n3k在该问题的评论中所写,可能会发生异常

让我们考虑某个变量fieldnull,那么:

field = null;
runThisLater(()->field.method());
field = new SomeObject();
Run Code Online (Sandbox Code Playgroud)

不会崩溃,同时

field = null;
runThisLater(field::method);
field = new SomeObject();
Run Code Online (Sandbox Code Playgroud)

会在方法引用语句行处崩溃java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()',至少在 Android 上是这样。

今天的 IntelliJ 在建议进行重构时指出,重构“可能会改变语义”。

当我们“引用”特定对象的实例方法时,就会发生这种情况。为什么?让我们检查 15.13.3 的前两段 。方法参考的运行时评估

在运行时,方法引用表达式的求值类似于类实例创建表达式的求值,只要正常完成会生成对对象的引用。方法引用表达式的求值与方法本身的调用不同。

首先,如果方法引用表达式以ExpressionNamePrimary开头,则计算此子表达式。如果子表达式的计算结果为 null, NullPointerException则引发a ,并且方法引用表达式突然完成。如果子表达式突然完成,则方法引用表达式也会出于同样的原因突然完成。

对于 lambda 表达式,我不确定。最终类型是在编译时从方法声明派生的。这只是对正在发生的事情的简化。但是,我们假设该方法runThisLater已声明为 void runThisLater(SamType obj),其中SamType是某个函数接口。然后runThisLater(()->field.method());翻译成这样:

runThisLater(new SamType() {  
    void doSomething() { 
        field.method();
    }
});
Run Code Online (Sandbox Code Playgroud)

附加信息: