2

I'm curious why is it possible to do this (at least on Java 8):

Optional.of(null).orElse("something");

Optional.of(null) is basically a guaranteed null pointer. Making possible to invoke .orElse() on it gives a clumsy developer unexpected holes to fall into. I've been looking around to see if there's any justification to this. Maybe there's some scenario that this is supposed to address?

kaya3
  • 47,440
  • 4
  • 68
  • 97
Tan Kim Loong
  • 980
  • 7
  • 14
  • 9
    `javac` has no compiler support for checking _if_ you pass a null, this is not very different than to _any_ other method call. – Eugene Oct 31 '19 at 15:30
  • 4
    @Eugene I'd say that the problem is more that `javac` has no sense of the *semantics* of `Optional` so it doesn't know what that line means in practice. – Federico klez Culloca Oct 31 '19 at 15:31
  • `orElse` is like any method, it is based on the type of the variable, not the instance itself. So here, you have an `Optional`, that's all. The exception that will occurs is a runtime exception so it may or may not happen, that's not on the compiler hand, it is in yours – AxelH Oct 31 '19 at 15:35
  • You may like Kotlin if you don't like null pointer issues. It is much like Java but it avoids some pitfalls. – Maarten Bodewes Oct 31 '19 at 15:47
  • 3
    Why do you focus on the ability to invoke `.orElse()` when the actual error is the expression `Optional.of(null)` which will fail at runtime? Why do you think, allowing `Optional.of(null)` without `.orElse()` would be better? – Holger Oct 31 '19 at 16:19
  • I agree that this is a reasonable question. There are IDEs like IntelliJ IDEA which will warn you about all sorts of probable-mistakes like this based on semantically-aware static checking. It would be possible for `javac` to do the same checks in principle, and it's reasonable to ask why it doesn't. – kaya3 Oct 31 '19 at 16:21
  • 3
    @kaya3 because `javac` is not an IDE. – Holger Oct 31 '19 at 16:23
  • I don't understand your comment. I didn't ask any "why" question, or any question at all. I am well aware that `javac` is not an IDE, and nothing I wrote suggests otherwise. – kaya3 Oct 31 '19 at 17:40

3 Answers3

10

You can do this for the same reason you can write ((String) null).length(). The Java compiler has a static checker for certain things like type-safety, but does not check for or reject code which provably throws a NullPointerException. If it did, the code throw new NullPointerException(); would not be allowed either.

It's not generally the case that code which throws an exception is a mistake. While a static checker could detect this kind of thing and warn about it, it would be bad if this was an error. Some IDEs will generate their own warnings about probable-mistakes, but you have to decide what is or isn't worth checking for.

Detecting this probable-mistake requires a static checker designed with knowledge of the behaviour of the Optional.of method. The authors of the Java Language Specification have made the decision to warn for things like unsafe casts, but not for anything which depends on the semantics of the methods being used. One advantage of this decision is that it means the JLS can be maintained separately from the Java Class Library documentation.

kaya3
  • 47,440
  • 4
  • 68
  • 97
7

There are languages where there is support for null checking right into the compiler (or even better languages that don't have such a "thing" at all) - this is burned into the compiler.

javac is not a such compiler and the be frank - I don't want it to become like this either. There are languages where there is syntax like ! or ? or !? - when I read this code it either screams on me or asks me something (this is my opinion).

Now, recall that Optional is designed for return types, so inside your method body you use an Optional chain of methods. If you really want to check if something is null or not there is Objects::requireNonNull - use that; if you are unsure if something is null or not, you could use Optional::ofNullable.

I know and read the arguments for Optional::of and Optional::ofNullable, I still don't like it, I wish it could be just the latter. But hey, people don't have to agree all the time: we do have Optional::isEmpty and Optional::isPresent. Is that a good decision? I don't know.

Like any other method call, you can pass null to any object reference, Optional is no different. Could they add semantics for the javac to support only Optional::of and null checking? Probably. How many people would request such a support for and this feature please?

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • 1
    Regarding `Optional::isEmpty` and `Optional::isPresent`, I think it is a good design choice to have both. `isEmpty` is a standard method for container types, and `isPresent` is useful to be passed as a method reference to `Stream.filter`, so you don't have to write `.filter(o -> !o.isEmpty())` as a lambda function every time. – kaya3 Oct 31 '19 at 15:52
  • 1
    @kaya3 Sometimes I wish `Stream` had some sort of "`exclude(Predicate)`" convenience method—it could simply delegate to `filter(predicate.negate())`. Though your argument obviously doesn't just apply to `Stream`. – Slaw Oct 31 '19 at 16:11
4
Optional.of(null).orElse();

Obviously the above code will always cause an exception. But what about:

Optional.of(someMethod()).orElse();

with

Object someMethod() { return null; }

You probably think: sure, same thing.

Well, actually that is wrong. What if a subclass overrides someMethod() to return a value other than null?! Thus you can't decide at compile time whether that method will always return null at runtime.

Long story short: there are plenty of obvious, and also less obvious situations where the compiler could apply all kinds of techniques, like data flow analysis to determine whether code will result in a runtime exception or not. But there are also many others where that isn't possible.

The point here: it is up to the people defining the language to determine what they expect compilers to care about.

The java people opted for a simple compiler. One that compiles fast, and that isn't overly complicated to implement. Why? Because the compiler simply avoid overly complicated checking.

GhostCat
  • 137,827
  • 25
  • 176
  • 248