4.1 在代码中使用Lambda表达式

2.5节介绍了如何赋予Lambda表达式函数接口的类型,以及该类型的推导方式。从调用Lambda表达式的代码的角度来看,它和调用一个普通接口方法没什么区别。

让我们来看一个日志系统中的具体案例。在slf4jlog4j等几种常用的日志系统中,有一些记录日志的方法,当日志级别不低于某个固定级别时就会开始记录日志。如此一来,在日志框架中设置类似void debug(String message)这样的方法,当级别为debug时,它们就开始记录日志消息。

问题在于,频繁计算消息是否应该记录日志会对系统性能产生影响。程序员通过显式调用isDebugEnabled方法来优化系统性能,如例4-1所示。即使直接调用debug方法能省去记录文本信息,也仍然需要调用expensiveOperation方法,并且需要将执行结果和已有字符串连接起来,因此,使用if语句显式判断,可以让程序跑得更快。

例4-1 使用isDebugEnabled方法降低日志性能开销

  1. Logger logger = new Logger();
  2. if (logger.isDebugEnabled()) {
  3. logger.debug("Look at this: " + expensiveOperation());
  4. }

这里我们想做的是传入一个Lambda表达式,生成一条用作日志信息的字符串。只有日志级别在调试或以上级别时,才会执行该Lambda表达式。使用这个方式重写上面的代码,如例4-2所示:

例4-2 使用Lambda表达式简化日志代码

  1. Logger logger = new Logger();
  2. logger.debug(() -> "Look at this: " + expensiveOperation());

那么在Logger类中该方法是如何实现的呢?从类库的角度看,我们可以使用内置的Supplier函数接口,它只有一个get方法。然后通过调用isDebugEnabled判断是否需要记录日志,是否需要调用get方法,如果需要,就调用get方法并将结果传给debug方法。由此产生的代码如例4-3所示。

例4-3 启用Lambda表达式实现的日志记录器

  1. public void debug(Supplier<String> message) {
  2. if (isDebugEnabled()) {
  3. debug(message.get());
  4. }
  5. }

调用get()方法,相当于调用传入的Lambda表达式。这种方式也能和匿名内部类一起工作,如果用户暂时无法升级到Java 8,这种方式可以实现向后兼容。

值得注意的是,不同的函数接口有不同的方法。如果使用Predicate,就应该调用test方法,如果使用Function,就应该调用apply方法。