Lambda表达式与方法引用

时间:2014-06-30 10:14:59

标签: java intellij-idea java-8

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

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

3 个答案:

答案 0 :(得分:144)

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

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

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

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

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

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

最后,我们发现大多数人起初远离方法引用,因为他们觉得比lambdas更新,更怪,因此最初发现它们“不太可读”,但结束了时间,当他们习惯了语法时,通常会改变他们的行为,并在他们可以的时候倾向于方法引用。所以请注意,你自己的主观初始“不太可读”的反应几乎肯定会带来一些熟悉偏见的方面,你应该给自己一个机会在提出风格意见之前对两者都感到满意。

答案 1 :(得分:10)

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

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

答案 2 :(得分:1)

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

让我们考虑一些变量 field 是未初始化的字段,然后:

field = null;
runThisLater(()->field.method());
field = new SomeObject();

不会崩溃,而

field = null;
runThisLater(field::method);
field = new SomeObject();

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

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

当对特定对象的实例方法进行“引用”时会发生这种情况。为什么? 让我们检查前两段 15.13.3. Run-Time Evaluation of Method References

<块引用>

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

首先,如果方法引用表达式以 ExpressionNamePrimary,这个子表达式被求值。 如果子表达式的计算结果为 null,则 NullPointerException 引发,方法引用表达式完成 突然。如果子表达式突然完成,则方法引用表达式 出于同样的原因突然完成。

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

runThisLater(new SamType() {  
    void doSomething() { 
        field.method();
    }
});

附加信息: