【问题标题】:How to make method argument evaluation lazy? [duplicate]如何使方法参数评估变得懒惰? [复制]
【发布时间】:2020-07-30 07:52:23
【问题描述】:

我的代码中有一个调试日志语句,我将代价高昂的方法调用的结果传递给该语句。

log.debug("Data - {}", obj.costlyMethodCall());

现在,即使调试日志被禁用,obj.costlyMethodCall() 将始终被评估为作为方法参数传递。

缓解这种情况的一种方法是明确检查是否启用了调试日志记录

if (log.isDebugEnabled()) {
  log.debug("Data - {}", obj.costlyMethodCall());
}

但这会降低代码的可读性。

这里有没有更好的方法来使 obj.costlyMethodCall() 的调用变得懒惰或有条件?

【问题讨论】:

  • 使用Supplier 或明确检查是否启用了调试。它降低了可读性,但前提是它被大量使用。如果它被大量使用,则表明如果您总是记录昂贵的方法调用,则说明您存在设计问题。

标签: java


【解决方案1】:

通常这可以通过让一个方法接受类似于 Supplier 而不是具体的 Object 的方法来解决,这样你就可以传入类似的东西

log.debug("Data - {}", () -> obj.costlyMethodCall());

log.debug 类似于

public void debug(String message, Supplier<Object> supplier) {
    Object value = supplier.get(); // costlyMethodCall is only called here
    // ...
}

【讨论】:

  • 使用 lambda 并不能提高可读性,恕我直言。
  • 我绝对喜欢它,而不是将单行日志语句变成三行 if-with-log-statement,尤其是因为另一个 if 增加了该方法的复杂性并需要测试确保当对象突然变为null 等时,日志语句本身不会产生错误。
  • lambda 将if 向下移动,因此测试复杂性不会改变。 (有点混淆发生的事情)
  • 好吧,当然,但是如果调试模式从“我的”代码中启用,它会移动条件,所以我不必对启用和禁用的调试模式进行特定测试 - 仅此而已正确的值被传递给记录器
【解决方案2】:

如果您已经广泛使用日志记录框架,那么按照Smutje 的建议定义自己的方法可能不是一个实际的选择。

相反,定义一个静态方法,例如:

static <T> Supplier<T> lazy(Supplier<? extends T> delegate) {
  return new Supplier<T>() {
    @Override public T get() { return delegate.get(); }
    @Override public String toString() { return Objects.toString(get()); }
  };
}

然后将此作为参数提供给您的调用:

log.debug("Data - {}", lazy(() -> obj.costlyMethodCall()));

如果您不提供覆盖toString()Supplier,则日志库无法(必然)知道它需要评估供应商以构建toString()Ideone example.

【讨论】:

  • Smutje 没有定义新的日志记录方法...新的日志记录框架,如 log4j 2.4 提供了这种方法。因此,无需定义自定义方法。
  • @RUAROThibault 没有提到具体的日志框架,nvm 一个版本。
【解决方案3】:

是的,您可以使用 lamdba 表达式来延迟。新版本的记录器使用这种方法,比如 log4j 2.4。您不需要提供新的方法,如果可以的话,只需使用更新的日志 API。

您可以执行以下操作:

log.debug("Data - {}", () -> obj.costlyMethodCall());

他们在最近的记录器中添加了这个,你需要延迟字符串的使用,而不用if(log.isDebugEnabled())污染你的代码...

你传递了一个Supplier,它将返回字符串,只有一次被调用。

如果需要,可以查看此链接:https://garygregory.wordpress.com/2015/09/16/a-gentle-introduction-to-the-log4j-api-and-lambda-basics/

【讨论】:

  • 这肯定只是让字符串评估变得懒惰,而不是costlyMethodCall()
  • 我正在更新:)
  • toString()Supplier 不一定调用供应商。
  • @Andy Turner 你是什么意思?
  • 在我的回答中查看 Ideone 示例。
猜你喜欢
  • 2019-05-13
  • 1970-01-01
  • 2019-01-07
  • 1970-01-01
  • 2013-03-20
  • 2014-01-04
  • 2015-09-06
  • 1970-01-01
  • 2016-12-14
相关资源
最近更新 更多