Effective Java 第三版读书笔记——条款 43:方法引用优于 lambda 表达式

lambda 优于匿名类的主要优点是它更简洁。Java 提供了一种生成函数对象的方法,比 lambda 还要简洁,那就是方法引用( method references)。下面是一段代码片段,它维护一个从任意键到 Integer 的 map。如果将该值解释为键的实例个数,则该程序是一个多重集合的实现。该代码的功能是,根据键找到整数值,然后在此基础上加 1:

map.merge(key, 1, (count, incr) -> count + incr);

请注意,此代码使用 merge 方法,该方法已添加到 Java 8 的 Map 接口中。如果没有给定键的映射,则该方法只是插入给定值;如果映射已经存在,则 merge 方法将给定函数应用于当前值和给定值,并用结果覆盖当前值。

这行代码很好读,但仍然有一些样板的味道。参数 count 和 incr 不会增加太多价值,并且占用相当大的空间。事实上,这个 lambda 告诉你的所有事情只是函数返回两个参数的和。从 Java 8 开始,Integer 类(和所有其他包装数字基本类型)提供了一个静态方法 sum,和上面的 lambda 做了完全相同的事情。我们可以简单地传递一个对这个方法的引用,并以较少的视觉混乱得到相同的结果:

map.merge(key, 1, Integer::sum);

方法的参数越多,你就可以通过方法引用消除越多的样板。然而,在一些 lambda 中,你选择的参数名称提供了有用的文档,使得 lambda 比方法引用更具可读性和可维护性,即使 lambda 看起来更长。

如果你使用 IDE 编程,它将提供把 lambda 替换为方法引用的功能。通常情况下,你应该接受这个提议。偶尔,lambda 会比方法引用更简洁。这种情况经常发生在方法与 lambda 在相同的类中。例如,考虑这段代码,它被假定出现在一个名为 GoshThisClassNameIsHumongous 的类中:

service.execute(GoshThisClassNameIsHumongous::action);

下面是等价的 lambda 代码:

service.execute(() -> action());

使用方法引用的代码段既不比使用 lambda 的代码片段更短也不清晰,所以应该使用后者。在类似的代码行中,Function 接口提供了一个通用的静态工厂方法来返回标识函数 Function.identity()。使用等效的 lambda 内联代码通常更短,更清晰:x -> x

许多方法引用是指静态方法,但有四种方法不是静态方法。 其中两个是特定(bound)和任意(unbound)对象方法引用。在特定对象引用中,接收对象在方法引用中指定。特定对象引用在本质上与静态引用类似:函数对象与引用的方法具有相同的参数。在任意对象引用中,接收对象在应用函数对象时通过方法的声明参数之前的附加参数指定。任意对象引用通常用作流管道(pipelines)中的映射和过滤方法(条款 45)。最后,对于类和数组,有两种构造方法引用。构造方法引用用作工厂对象。下表总结了所有五种方法引用:

方法引用类型 举例 等价的 lambda
Static Integer::parseInt str -> Integer.parseInt(str)
Bound Instant.now()::isAfter Instant then = Instant.now();

t -> then.isAfter(t)
Unbound String::toLowerCase str -> str.toLowerCase()
Class Constructor TreeMap<K,V>::new () -> new TreeMap<K,V>
Array Constructor int[]::new len -> new int[len]

总之,方法引用通常为 lambda 提供一个更简洁的选择。如果方法引用看起来更简短更清晰,请使用它们;否则,还是坚持使用 lambda

posted @ 2019-02-09 13:08  LeeFire  阅读(198)  评论(0编辑  收藏  举报