| First of all, the use of isPresent is a red flag that optionals aren't used right. You'd typically use a sequence of map, flatMap and orElse calls. For example, this is some controller code I'm currently shipping: return Drawing.getForLocation(id)
.map(d -> ok(Json.toJson(d)))
.orElse(notFound("No drawing for location " + id));
That construct is more readable to me than an if(drawing != null) check. The cognitive load issue is a bit of a red herring. I'm used to optionals now and they're no more of a cognitive load than an if construct. But that's eye of the beholder stuff, so let's park the cognitive load issue for now.Getting down to the meat of your argument, why is optional better than an if not null check or an exception? I think anyone can see why it's better than throwing an exception. Optional makes it obvious to the caller that (a) there can be no response, and (b) what happens exactly when there's no response. With exceptions, either they're unchecked, in which case you don't know what exception to catch without peeking, and it's really easy to forget to add a try/catch handler at all; or it's checked, but even in that case the handling code is uglier than an equivalent optional construct. The more interesting question is why it is better than deliberately returning null. Again, it's about signaling. A library author can signal to the caller that they should expect not to get a value sometimes. Yes, it's possible to return null from a method that returns an optional, and this will cause an NPE, but in that case the error is the library's, not the callers. In making API's, you want to encourage correct use, and optionals help do that. Optionals in java suffer from the same problem as most API's in java do though. They're uglier than they need to be, and some common use patterns are awkward. For example, in my spark code in scala I'll use constructs like this one to convert a stream between types where the conversion might fail for some elements (and I don't care about the ones where it does): val someIntermediate = someStream.flatMap(convertMaybe)
That works because Option can be handed to flatMap directly.But in Java streams, you can't do that, and you end up with constructs like this: someStream.map(convertMaybe).flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())
Which is insane. In Java 9 they're kind of sort of fixing this and it's going to be someStream.map(convertMaybe).flatMap(Optional::stream)
Which is still ugly though.Any way, I think optional has its uses in Java. It's definitely something that helps when building out robust API's. But where optionals would be most valuable, the combination with streams and futures, is where they're painful to work with. That's par for the course in Java though. Oracle's not actually all that good at designing API's. Usually the first attempt at something turns out so broken it has to be replaced wholesale later (like java.util.Date), or it's so ugly it feels bad to use it (CompletionStage? seriously?). But, to each their own, I'm sure my attempt at it would be even worse, and despite the warts I still like Java 8 overall. |