Is it possible to LOG different messages based on which step null is encountered while doing nested null check with Optional and map

huangapple go评论75阅读模式
英文:

Is it possible to LOG different messages based on which step null is encountered while doing nested null check with Optional and map

问题

I have a nested object which can return a null at any point of time.

Thanks to Optional and map we can now do nested calls without having to put null checks after every get.

I have a very unique requirement where I need to know at which step exactly did I encounter a null object, for example (Copied from StackOverflow):

Optional.of(new Outer())
  .map(Outer::getNested)
  .map(Nested::getInner)
  .map(Inner::getFoo)
  .ifPresent(System.out::println);

How can I LOG a different kind of log message depending on when and where I encounter a null value?

The code below is not valid but I am just trying to explain how it might look like programmatically:

Optional.of(outerObject).else(LOG.error("Outer was null"))
  .map(Outer::getNested).else(LOG.error("Nested was null"))
  .map(Nested::getInner).else(LOG.error("Inner was null"))
  .map(Inner::getFoo).else(LOG.error("Foo was null"))
  .ifPresent(System.out::println);
英文:

I have a nested object which can return a null at any point of time.

Thanks to Optional and map we can now do nested calls without having to put null checks after every get.

I have a very unique requirement where I need to know at which step exactly did I encounter a null object for e.g. (Copied from StackOverflow)

Optional.of(new Outer())
  .map(Outer::getNested)
  .map(Nested::getInner)
  .map(Inner::getFoo)
  .ifPresent(System.out::println);

How can I LOG a different kind of log message depending on when and where I encounter a null value?

The code below is not valid but I am just trying to explain how it might look like programmatically:

Optional.of(outerObject).else(LOG.error("Outer was null"))
  .map(Outer::getNested).else(LOG.error("Nested was null"))
  .map(Nested::getInner).else(LOG.error("Inner was null"))
  .map(Inner::getFoo).else(LOG.error("Foo was null"))
  .ifPresent(System.out::println);

答案1

得分: 1

你可以使用异常处理来实现所需的行为,代码如下:

public class Test {
    public static void main(String[] args) {
        Outer outer = new Outer(new Nested(new Inner("value")));
//        Outer outer = new Outer(new Nested(new Inner(null)));
//        Outer outer = new Outer(new Nested(null));
//        Outer outer = new Outer(null);
//        Outer outer = null;
        try {
            Optional.ofNullable(outer).or(() -> throwEx("Outer was null"))
                    .map(Outer::nested).or(() -> throwEx("Nested was null"))
                    .map(Nested::inner).or(() -> throwEx("Inner was null"))
                    .map(Inner::foo).or(() -> throwEx("Foo was null"))
                    .ifPresent(System.out::println);
        } catch (NullValueException e) {
            System.out.println(e.getMessage());
        }
    }

    private static <T> T throwEx(String msg) {
        throw new NullValueException(msg);
    }
}

class NullValueException extends RuntimeException {
    public NullValueException(String msg) {
        super(msg);
    }
}

record Outer(Nested nested) {
}

record Nested(Inner inner) {
}

record Inner(String foo) {
}

注意:这只是代码的翻译部分,不包括问题或其他内容。

英文:

You can achieve the required behaviour with some exception handling like this:

public class Test {
    public static void main(String[] args) {
        Outer outer = new Outer(new Nested(new Inner(&quot;value&quot;)));
//        Outer outer = new Outer(new Nested(new Inner(null)));
//        Outer outer = new Outer(new Nested(null));
//        Outer outer = new Outer(null);
//        Outer outer = null;
        try {
            Optional.ofNullable(outer).or(() -&gt; throwEx(&quot;Outer was null&quot;))
                    .map(Outer::nested).or(() -&gt; throwEx(&quot;Nested was null&quot;))
                    .map(Nested::inner).or(() -&gt; throwEx(&quot;Inner was null&quot;))
                    .map(Inner::foo).or(() -&gt; throwEx(&quot;Foo was null&quot;))
                    .ifPresent(System.out::println);
        } catch (NullValueException e) {
            System.out.println(e.getMessage());
        }
    }

    private static &lt;T&gt; T throwEx(String msg) {
        throw new NullValueException(msg);
    }
}

class NullValueException extends RuntimeException {
    public NullValueException(String msg) {
        super(msg);
    }
}

record Outer(Nested nested) {
}

record Nested(Inner inner) {
}

record Inner(String foo) {
}

答案2

得分: 1

如果这只是一次性的事情,我会编写一个帮助方法来“包装”方法引用。包装器将返回包装的函数返回的内容,但如果包装的方法返回null,它还会记录一条消息。

```java
private static <T, R> Function<T, R> withNullMessage(Function<? super T, ? extends R> function, String message) {
    return t -> {
        R r = function.apply(t);
        if (r == null) {
            Log.error(message);
        }
        return r;
    };
}
Optional.of(foo)
        .map(withNullMessage(Foo::getBar, "Bar is null!"))
        .map(withNullMessage(Bar::getBaz, "Baz is null!"))
        ...

请注意,这不处理foo为null的情况。如果foo为null,这将引发异常。要处理这种情况,您可以从一个肯定不为null的对象开始,

Optional.of("")
        .map(withNullMessage(x -> foo, "Foo is null!"))
        .map(withNullMessage(Foo::getBar, "Bar is null!"))
        .map(withNullMessage(Bar::getBaz, "Baz is null!"))

或者您可以编写自己的of方法来记录null。

这种方法的另一个缺点是它不适用于flatMap。例如,这不会按您期望的那样工作:

.flatMap(withNullMessage(Foo::thisReturnsAnotherOptional, "...")

您需要另一个包装方法来处理这种情况。

如果您经常需要这种类型的功能,可能值得编写自己的类似Optional的类型,其map方法接受额外的参数。


<details>
<summary>英文:</summary>

If this is a one-off thing, I would write a helper method that &quot;wraps&quot; the method references. The wrapper would return what the wrapped function returns, but if the wrapped method returns null, it also logs a message.

private static <T, R> Function<T, R> withNullMessage(Function<? super T, ? extends R> function, String message) {
return t -> {
R r = function.apply(t);
if (r == null) {
Log.error(message);
}
return r;
};
}


Optional.of(foo)
.map(withNullMessage(Foo::getBar, "Bar is null!"))
.map(withNullMessage(Bar::getBaz, "Baz is null!"))
...


Note that this does not handle the case where `foo` is null. If `foo` is null, this will throw an exception. To handle this, you can start with a definitely-not-null thing,

Optional.of("")
.map(withNullMessage(x -> foo, "Foo is null!"))
.map(withNullMessage(Foo::getBar, "Bar is null!"))
.map(withNullMessage(Bar::getBaz, "Baz is null!"))


Or you can write your own `of` that logs nulls.

Another drawback of this is that it doesn&#39;t work with `flatMap`s. e.g. this does not work as you&#39;d expect:

    .flatMap(withNullMessage(Foo::thisReturnsAnotherOptional, &quot;...&quot;))

You would need another wrapper method to handle that case.

If you need this sort of thing a lot, it&#39;s probably worth it to write your own `Optional`-like type, whose `map` methods take an extra argument.

</details>



# 答案3
**得分**: 1

根据前面清扫工的答案中提到的一次性错误算法,我们可以实现如下。这将处理角落的空值情况以及其他情况:

```java
public static void clientMethod() {
    Outer validOuter = new Outer(new Nested(new Inner("s")));
    Outer nullNested = new Outer(new Nested(null));
    Optional.ofNullable(nullNested)
            .map(outer -> eam(outer, Function.identity(), "提取的值:Outer 为 null"))
            .map(outer -> eam(outer, Outer::getNested, "提取的值:Nested 为 null"))
            .map(nested -> eam(nested, Nested::getInner, "提取的值:Inner 为 null"))
            .map(inner -> eam(inner, Inner::getFoo, "提取的值:Foo 为 null"))
            .ifPresent(System.out::println);
}

public static <T, R> R eam(T object, Function<T, R> extractor, String extractedValueNullMessage) {
    if (object != null) {
        R extractedValue = extractor.apply(object);
        if (extractedValue == null) {
            LOG.error(extractedValueNullMessage);
        }
        return extractedValue;
    }
    return null;
}

请注意,代码中的注释和变量名已经被翻译。

英文:

Following the algorithm of one-off error mentioned in previous Sweeper's answer we could implement as below.This will take care of corner null cases as well

    public static void clientMethod() {
        Outer validOuter = new Outer(new Nested(new Inner(&quot;s&quot;)));
        Outer nullNested = new Outer(new Nested(null));
        Optional.ofNullable(nullNested)
                .map(outer-&gt;eam(outer, Function.identity(),&quot;Extracted value: Outer was null&quot;))
                .map(outer -&gt;eam(outer, Outer::getNested,&quot;Extracted value:Nested was null&quot;))
                .map(nested-&gt;eam(nested, Nested::getInner,&quot;Extracted value:Inner was null&quot;))
                .map(inner-&gt;eam(inner, Inner::getFoo,&quot;Extracted value:Foo was null&quot;))
                .ifPresent(System.out::println);
    }

    public static &lt;T,R&gt; R eam (T object,Function&lt;T,R&gt; extracter,String extractedValueNullMessage){
        if(object != null){
            R extractedValue = extracter.apply(object);
            if (extractedValue == null){
                LOG.error(extractedValueNullMessage);
            }
            return extractedValue;
        }
        return null;
    }

huangapple
  • 本文由 发表于 2023年7月20日 14:23:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76727184.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定