A long time ago, there was a function
public static Foo getFoo(Bar bar) {
return bar.getFoo();
}
And people just couldn't agree what should happen if bar was null.
First of all, there would be people who would claim that violating the intent of the function should be penalized with a checked exception.
public static Foo getFoo(Bar bar) throws FooNotFoundException {
if (bar == null) throw new FooNotFoundException();
return bar.getFoo();
}
The caller of the function would then be forced to take this scenario in account. It would have to catch the exception or rethrow it. It would be so forceful that it would annoy people, and soon they would argue that this shouldn't be a checked exception, but a runtime exception, to make it less forceful.
public static Foo getFoo(Bar bar) {
if (bar == null) throw new FooNotFoundException();
return bar.getFoo();
}
Some would claim that there would be no point in throwing an exception, since java would already throw an exception anyway: a Nullpointer exception.
Those who would fear unexpected exceptions, would just make their code more rebust and would add null
-checks at the start of their functions, which would simply return null
.
public static Foo getFoo(Bar bar) {
if (bar == null) return null;
return bar.getFoo();
}
Soon people would be arguing if it would be ok to return empty lists, or whether those empty lists should actually be null
values as well. "Surely, you don't want to check nullability before each and every iteration, do you?" would the other side argue. And soon, they would be creating all kind of constructs to avoid nullability entirely.
public static Foo getFoo(Bar bar) {
if (bar == null) return Foo.Empty;
return bar.getFoo();
}
Each approach would have a wide range of consequences. And those consequences would make it difficult to combine different approaches. Those consequences would result in coding rules where each individual rule would be connected to the next rule. The way they supported each other would give the impression that each and every rule was undisputable. And finally coding rules would become like religions with reasoning which only made sense inside the scope of the full ruleset.
Depending on your choice of ruleset you would have difficulties using certain frameworks. In the end, the empty-list with unchecked-exceptions religion became dominant. This religion can be summarized as follows:
- you should avoid returning
null
values.
- if a list is empty, you return it as is.
- if you iterate a list, you never have to check for null.
- a method should never throw a checked exception
- checked exceptions should be wrapped in runtime exceptions.
- strings shouldn't ever be
null
, instead they should be ""
.
And apparently this religion got so strong that it managed to influence the framework and language specification.
- compiler optimizations for empty lists
- an
Optional
class
- value types
Some external libraries and editors would actually try to re-unite the different teams by providing annotations (@Null
and @NotNull
). The IDE would just mark all violations for you. A simple but effective solution. Nevertheless, the JDK never included its own @Null
or @NotNull
, instead each library had to ship their own.
And taking all of this in account, right now, it is very unlikely that there will ever be an elvis operator in java. If you want to code in java, you better forget about null
.
Or to put it in the words of Tony Hoare (the inventor of null):
I call it my billion-dollar mistake. It was the invention of the null
reference in 1965. At that time, I was designing the first
comprehensive type system for references in an object oriented
language (ALGOL W). My goal was to ensure that all use of references
should be absolutely safe, with checking performed automatically by
the compiler. But I couldn't resist the temptation to put in a null
reference, simply because it was so easy to implement. This has led to
innumerable errors, vulnerabilities, and system crashes, which have
probably caused a billion dollars of pain and damage in the last forty
years.
Personally, I think this absolutely makes no sense, given the fact that every decent programming language has a null. Some even have multiple ones to indicate different kinds of nullabilities. After all, even in mathematics there are undefined values.
Anyway, if you don't have an elvis operator, you can still
Foo foo = bar == null ? null : bar.getFoo();
And this just fits perfectly in the spirit of java. After all, in 2021 java is a very explicit language.