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);
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.