I’ve just stumbled upon a warning generated by IntelliJ and I’m wondering, do I miss something or is IntelliJ just ignoring the right side of the following or clause?
Example Code:
Random random = new Random();
public void test(){
Optional<String> a = Optional.ofNullable(random.nextInt(10)>5?"something":null);
Optional<String> b = Optional.ofNullable(random.nextInt(10)>5?"something":null);
if(a.isPresent() || b.isPresent()){
log.info(a.orElse(b.get()));
}
}
The warning 'Optional.get()' without 'isPresent()' check is shown on the b.get().
I get that the or is evaluated from left to right in a lazy way but I would assume that either a or b has to have a value present as it’s checked explicitly in the if.
Is that an error in IntelliJ’s static code analysis?
>Solution :
Yes-ish. || short-circuits but orElse doesn’t, so b.get still runs and raises an exception if b is absent, even if a is present. That’s why Java provides orElseGet, which takes a Supplier (i.e. a zero-argument function) to be called if we actually need to run the ‘else’ part.
log.info(a.orElseGet(() -> b.get()));
Now, I don’t have IntelliJ available on this computer right now, but I suspect it will still complain. The static analysis engine (probably) doesn’t understand the interaction between orElseGet and ||. Generally, when I encounter situations like this, I factor the offending code out into a separate function.
void onFirst<T>(Optional<T> first, Optional<T> second, Consumer<T> consumer) {
if (first.isPresent()) {
consumer.accept(first.get());
} else if (second.isPresent()) {
consumer.accept(second.get());
}
}
// Then call as
onFirst(a, b, (value) -> log.info(value));
A bit more wordy, but it passes static analysis muster. And the extra wordiness is off in a separate helper function that can be reused.