4

I need to use a getter which is 3 levels down in the object.

response.getServiceError().getErrorCode()

It is possible one or more objects could be NULL. Now, I am doing this

if (response != null && response.getServiceError() != null && response.getServiceError().getErrorCode() != null && response.getServiceError().getErrorCode().equals("errCode123")) {
     //doSomething;
}

Is there a better and/or elegant way to construct that if condition?

Naman
  • 27,789
  • 26
  • 218
  • 353
Trozan
  • 89
  • 2
  • 11
  • Not really. Only hacks with `Optional`. – Kayaman Sep 17 '20 at 21:21
  • There is always the `try { response.getServiceError().getErrorCode() } catch (NullPointerException e) { ... }`-hack. Proper way, however, would be to return `Optional`s – Turing85 Sep 17 '20 at 21:23
  • A proper elvis operator would be needed. `Optional` is way too cumbersome if you have deep nesting. – Kayaman Sep 17 '20 at 21:25
  • 2
    The best way to deal with this is not to return null. Why would the response be null? I can see a service error being null, but why would a service error lack an error code? All that said, you can at least eliminate the third null check by taking advantage of the fact that an `equals` method always safely returns false when passed a null argument, by using `"errCode123".equals(response.getServiceError().getErrorCode())`. – VGR Sep 17 '20 at 21:35

1 Answers1

9

Use Optional!

Optional.ofNullable(response)
    .map(Response::getServiceError)
    .map(ServiceError::getErrorCode)
    .filter(code -> code.equals("errCode123"))
    .ifPresent(code -> {
        // doSomething
    });

JDrost1818
  • 1,116
  • 1
  • 6
  • 23
  • 2
    "How succinct and handy!" – Kayaman Sep 17 '20 at 21:27
  • I sense some sarcasm... Lol. But it's not too bad. And if you have something that will be used a lot, you can extract it out to a function. But yeah, definitely not a straight replacement for an elvis op. – JDrost1818 Sep 17 '20 at 21:29
  • 3
    Close, but: 1) The 3rd `map()` should be `filter()`, because `equals()` returns a `boolean`, which is auto-boxed to `Boolean` by the `map()` return type, even when `false`, so a "not equals" condition is still "present" as a `Boolean.FALSE` object. --- 2) `ifPresent` takes a `Consumer`, so it doesn't have a return value. Remove the `return` statement. --- 3) You're missing a `)` on the 3rd `map()` call. – Andreas Sep 17 '20 at 21:34
  • 1
    Well it's not like [it hasn't been thought of](https://softwareengineering.stackexchange.com/questions/359414/why-were-null-safe-operators-e-g-elvis-operator-rejected-as-part-of-java-7). But then they started saying that it's "just as easy to use [Optional for it](https://stackoverflow.com/questions/52285813/how-to-implement-the-elvis-operator-in-java-8)", although `Optional` is [broken in some way](https://blog.developer.atlassian.com/optional-broken/) as things in Java tend to be (generics etc.)... (may contain sarcasm). – Kayaman Sep 17 '20 at 21:37
  • @Kayaman That last link seems to say that Optional is “broken” because it doesn’t have a mapEvenIfNull method, for the case where a function might want to turn an empty/null value back into a non-empty value. That doesn’t seem like a common case to me. – VGR Sep 18 '20 at 11:45
  • @VGR it's more about FP purity than real world application. – Kayaman Sep 18 '20 at 11:59
  • @VGR do you mean the `Optional#or` (not to be confused with `Optional#orElse`) functionality? – JDrost1818 Sep 18 '20 at 17:34
  • @JDrost1818 Yes, although to some extent the orElse* methods also provide at least some of that functionality. – VGR Sep 18 '20 at 17:38