为了账号安全,请及时绑定邮箱和手机立即绑定

如何处理 Java 可选链中空值的日志记录

如何处理 Java 可选链中空值的日志记录

哆啦的时光机 2022-11-02 15:20:26
假设我正在使用选项,并且我有:A a;....Optional.ofNullable(a)    .map(A::getB)    .map(B::getC)    .ifPresent(c -> ...) // do something with c但是,如果出于某种原因,我想在 B::getC 为空(或 a 为空)时记录。有一些成语来处理这个问题吗?我可以做一堆嵌套的 ifPresent 调用,但这非常冗长。我能想到的最好的方法是一个 FunctionalHelper 类,它包装了一些 Optional 方法并添加了其他方法来支持日志记录(log4j):private static final Logger LOG = //...private static final FunctionalHelper FH     = new FunctionalHelper(LOG, Level.DEBUG);FH.ofNullable(a, Level.WARN, "You gave me a null A"))    .map(FH.logNull(A::getB, "b was null for a: {}", a-> FH.args(a.getName()))    .map(B::getC)    .ifPresent(c -> ...) // do something with c它有效,但感觉有点像一个螺栓固定的解决方案。是否有一些成语(或至少是标准库)来处理这种事情?(我也想知道如何干净地处理链中抛出的检查异常,但也许这是一个单独的问题。)更新:作为对@Ole VV 的回应,这里是可选的示例(更新2:我对其进行了一些调整以匹配 log4j varags 供应商语义,但具有功能:private static final FunctionalHelper FH  = new FunctionalHelper(LOG, Level.DEBUG);void foo(A someA) {    FH.ofNullable(someA, Level.WARN, "You gave me a null A")            .map(FH.logNull(A::getB, "B was null for A: {}", A::getName))            .map(FH.logNull(B::getC, "C was null for B: {}", B::getName))            .ifPresent(c -> { /* do something with c */});}这是一个 If-else 实现:void foo2(A a) {    if (a == null) LOG.debug("You gave me a null A");    else {        B b = a.getB();        if (b == null) LOG.debug("B was null for a: {}", a::getName);        else {            String c = b.getC();            if (c == null) LOG.debug("C was null for a: {}", b::getName);            else { /* do something with c */ }        }    }}我会说前者更容易阅读。也更容易修改现有的可选链以在需要时添加一些东西。
查看完整描述

4 回答

?
桃花长相依

TA贡献1860条经验 获得超8个赞

正如@Ole 所说, Optional 并不意味着:


与其创建一个 Optional,不如创建一个单例流(我刚刚注意到 Stream::of 的存在)。


您失去了 Optional::map 仅在元素存在时执行的便利性,因此您自己有过滤器空值:


A a;

Stream.of(a)

  .peek(e->{if(e == null){LOG.debug("You gave me a null A");}})

  .filter(e->e != null) //Now you have to handle null values yourself

  .map(A::getB)

  .peek(e->{if(e == null){LOG.debug("B was null for A "+a.getName());}})

  .filter(e->e != null)

  .map(B::getC)

  .peek(e->{if(e == null){LOG.debug("C was null for B"+a.getB().getName());}})

  .filter(e->e != null)

  .findFirst() // Optional from now on.

如果您从可选开始,您可以执行以下操作:


Optional<A> optA;

Stream.of(optA.orElse(null))

stream 到 optional 的过渡是平滑的,而 optional 到 stream 则不是。


查看完整回答
反对 回复 2022-11-02
?
心有法竹

TA贡献1866条经验 获得超5个赞

你不应该使用这样的结构有两个原因:

  1. 首先,你不应该混搭nulland OptionalOptional真的是nullapi-wise 的替代品。当你返回时null,你就有问题了。A::getB应该返回一个Optional<B>,而不是一个null。好的,有时您可以从外部世界获取 API,而您没有动手A::getB,所以如果是这种情况,您只需像这样工作,但另一种方法是清理您的输入。

  2. 其次,您不应该记录null值。如果你记录null值,这意味着你有类似的问题NullPointerExceptions,这意味着你没有得到你所期望的。所以这基本上意味着你应该清理你的输入!在开发的时候有这种语句是可以的,但是在生产中,你应该能够在a.getB()返回null和b.getC()返回null时区别对待。这些天来,使用调试器通常比使用那些关于哪个值是null.

这两个问题都可以通过清理您的输入来解决。清理您的输入意味着您应该将不属于您的输入映射到您的输入。通常的答案是代码重复:API 代码和您的代码。嗯,是的:有 API 模型和你的模型,但它不是重复的代码:一个被清理,另一个没有!

但无论如何,使用简单的包装器完全可以实现您的期望。实际上,它与您编写的内容相似(即使您没有显示代码),但我想您已经找到了处理此问题的最佳方法:

static <T,U> Function<T, U> logIfReturnsNull(Function<? super T, ? extends U> function, String functionName) {

  return input -> {

    U result = function.apply(input);

    if (result == null) {

      log.debug("{} returned null for input {}", functionName, input);

    }

    return result;

  };

}

然后你像这样使用它:


Optional.ofNullable(a)

  .map(logIfReturnsNull(A::getB, "A::getB"))

  .map(logIfReturnsNull(B::getC, "B::getC"))

  .ifPresent(c -> ...)


查看完整回答
反对 回复 2022-11-02
?
慕勒3428872

TA贡献1848条经验 获得超6个赞

您可以与返回一些默认值Optional.orElseGet()的 a 一起使用(不确定我的类型是否完全正确)。Supplier<A>A


Supplier<A> nonNullASupplier = () -> {

    LOGGER.log("Invoking supplier because A was null");

    return new A();

}

然后在调用代码中:


Optional.ofNullable(a)

    .orElseGet(nonNullASupplier)

    .map(A::getB)

    .map(B::getC)

    .ifPresent(c -> ...) // do something with c

同样不是 100% 确定这里有什么类型A,B但这是基本思想。


查看完整回答
反对 回复 2022-11-02
?
暮色呼如

TA贡献1853条经验 获得超9个赞

我不知道这是否适合你,但也许你可以使用空对象模式。


class NullA extends A {

  B getB() {

     return new NullB();

  }

}

class NullB extends B {

  C getC() {

     return new NullC();

  }

}

class NullC extends C {

}

然后使用orElseGet返回空对象:


Optional.ofNullable(a)

    .orElseGet(new NullA())

    .map(A::getB)

    .map(B::getC)


查看完整回答
反对 回复 2022-11-02
  • 4 回答
  • 0 关注
  • 124 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信