Null Used With Optional
ID |
java.null_optional |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Java |
Tags |
null-safety, reliability |
Description
Reports four shapes where null is used together with Optional, reintroducing the
NullPointerException that Optional exists to remove: a local variable or field declared
Optional initialised to null; a ==/!= null comparison against a name declared Optional;
a return null; inside a method declared to return Optional; and a @Nullable annotation on a
field, parameter or method whose declared type is Optional.
Rationale
Optional is a container that models the presence or absence of a value without resorting to
null. Mixing the two defeats that purpose in two directions:
// Bug: an Optional variable can now be null OR empty -- two ways to mean "absent"
Optional<Config> config = null;
if (config != null) { // Bug: bypasses Optional's own emptiness check
apply(config);
}
// Bug: callers that trust the signature and skip a null-check get an NPE anyway
Optional<Config> loadConfig() {
if (!configFile.exists()) {
return null; // should be Optional.empty()
}
return Optional.of(read(configFile));
}
// Bug: an Optional return value should never itself be null
@Nullable
Optional<Config> findConfig() {
return configFile.exists() ? Optional.of(read(configFile)) : Optional.empty();
}
Once any of these shapes ships, every caller must null-check and empty-check the same value,
which is exactly the boilerplate Optional was introduced to remove.
Remediation
Use Optional.empty() wherever an absent value would otherwise be null, and call
isPresent()/ifPresent() instead of comparing against null:
Optional<Config> config = Optional.empty();
config.ifPresent(NullOptionalExample::apply);
Optional<Config> loadConfig() {
if (!configFile.exists()) {
return Optional.empty();
}
return Optional.of(read(configFile));
}