英文:
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("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) {
}
答案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 "wraps" 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't work with `flatMap`s. e.g. this does not work as you'd expect:
.flatMap(withNullMessage(Foo::thisReturnsAnotherOptional, "..."))
You would need another wrapper method to handle that case.
If you need this sort of thing a lot, it'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("s")));
Outer nullNested = new Outer(new Nested(null));
Optional.ofNullable(nullNested)
.map(outer->eam(outer, Function.identity(),"Extracted value: Outer was null"))
.map(outer ->eam(outer, Outer::getNested,"Extracted value:Nested was null"))
.map(nested->eam(nested, Nested::getInner,"Extracted value:Inner was null"))
.map(inner->eam(inner, Inner::getFoo,"Extracted value:Foo was null"))
.ifPresent(System.out::println);
}
public static <T,R> R eam (T object,Function<T,R> extracter,String extractedValueNullMessage){
if(object != null){
R extractedValue = extracter.apply(object);
if (extractedValue == null){
LOG.error(extractedValueNullMessage);
}
return extractedValue;
}
return null;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论