Java Lambda表达式与方法引用

Java Lambda表达式与方法引用,java,intellij-idea,java-8,Java,Intellij Idea,Java 8,IntelliJ一直建议我用方法引用替换lambda表达式 两者之间有客观上的区别吗 由多个语句组成的长lambda表达式可能会降低代码的可读性。在这种情况下,在方法中提取这些语句并引用它可能是更好的选择 另一个原因可能是可重用性。您可以构造一个方法并从代码的不同位置调用它,而不是复制和粘贴几个语句的lambda表达式。让我从一些角度来解释为什么我们在语言中添加了此功能,而显然我们并不需要(所有方法引用都可以表示为lambda) 请注意,没有正确的答案。任何说“始终使用方法引用而不是lambda

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


两者之间有客观上的区别吗

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


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

让我从一些角度来解释为什么我们在语言中添加了此功能,而显然我们并不需要(所有方法引用都可以表示为lambda)

请注意,没有正确的答案。任何说“始终使用方法引用而不是lambda”或“始终使用lambda而不是方法引用”的人都应该被忽略

这个问题在精神上与“何时使用命名类与匿名类”非常相似?答案是一样的:当你发现它更具可读性时。当然有一些案例肯定是一个或另一个,但中间有大量的灰色,必须使用判断。 方法refs背后的理论很简单:名称很重要。如果一个方法有一个名称,那么通过名称来引用它,而不是通过一包命令式代码来调用它,通常(但不总是!)更清晰易读

关于性能或字符计数的争论大多是转移注意力的,你应该忽略它们。目标是编写非常清晰的代码。通常(但不总是!)方法引用在这个指标上获胜,所以我们将它们作为一个选项包括在内,以便在这些情况下使用

关于方法引用是澄清还是混淆意图的一个关键考虑因素是,从上下文看,所表示的函数的形状是否明显。在某些情况下(例如,
map(Person::getLastName)
,从上下文中可以很清楚地看出,需要一个将一个对象映射到另一个对象的函数,在这种情况下,方法引用会发光。在其他情况下,使用方法引用要求读者想知道所描述的是哪种函数;这是一个警告信号,即lambda可能更可读,即使它更长


最后,我们发现,大多数人一开始会避开方法ref,因为它们比lambdas感觉更新、更奇怪,所以一开始觉得它们“可读性较差”,但随着时间的推移,当他们习惯了语法时,通常会改变他们的行为,并在可能的情况下倾向于方法引用。因此,请注意,您自己的主观首字母“可读性较差”几乎可以肯定的是,这种反应会带来一些熟悉度偏见,在发表风格观点之前,你应该给自己一个机会,让自己对这两种偏见都感到舒服。

正如用户stuchl4n3k在对问题的评论中所写的,可能会出现例外情况

让我们考虑一些变量<代码>字段< /> >是未初始化的字段,然后:

field=null;
运行thislater(()->field.method());
field=新的SomeObject();
不会崩溃,而

field=null;
runThisLater(字段::方法);
field=新的SomeObject();
将因java.lang.NullPointerException而崩溃:至少在Android上,尝试在方法引用语句行调用虚拟方法“java.lang.Class java.lang.Object.getClass()”

今天的IntelliJ在建议这种重构时指出“可能会改变语义”

当“引用”特定对象的实例方法时会发生这种情况。为什么? 让我们检查一下文章的前两段 :

在运行时,方法引用表达式的计算类似于 类实例创建表达式,只要正常完成生成引用 方法引用表达式的计算不同于调用 方法本身

首先,如果方法引用表达式以ExpressionName或 Primary,将计算此子表达式。如果子表达式的计算结果为null,则
NullPointerException
被引发
,方法引用表达式完成 突然。如果子表达式突然完成,则方法引用表达式 出于同样的原因突然完成

在lambda表达式的情况下,我不确定,最终类型是在编译时从方法声明中派生的。这只是对实际情况的简化。但是让我们假设方法
runThisLater
已声明为例如。
void runThisLater(SamType obj)
,其中SamType是一些函数接口,然后
runThisLater(()->field.method());
转换为类似以下内容:

runThisLater(新的SamType(){
void doSomething(){
field.method();
}
});
其他信息:

  • ,第3版,其中提到SAM
  • ,决赛

不太可能,我没有看到任何对象!当然,这看起来像是对我的静态调用!但是“难道你没有发现”或者……这是一个品味的问题,我更担心更多的技术方面。事实上,正如你已经说过的,这对我来说是一个很好的答案。不管怎样,正如IntelliJ所建议的,我想看到一个方法引用比一个lambda(但对我来说不是这样)更受欢迎。lambda表达式的代码被编译为合成方法,而方法引用可以正常工作(异常:特殊构造,如
Type[]::new
)。在运行时生成的匿名类将是相同的。JRE对它们没有任何区别。因此,使用方法引用将在编译代码中为您保存一个方法,另一方面,在进行逐步调试时,您不能停止使用它们…现在问题将被关闭…对我们所有人来说都太糟糕了