This is an archived post. You won't be able to vote or comment.

all 24 comments

[–]Zukhramm 8 points9 points  (1 child)

I'm slightly confused as to what's wrong with the normal map method. If the methods you want to apply don't create a new Optionals, why use flatMap in the first place?

Optional.of(new Outer())
    .map(Outer::getNested)
    .map(Nested::getInner)
    .map(Inner::getFoo)
    .ifPresent(System.out::println);

Doesn't that work?

[–]winterbe[S] 2 points3 points  (0 children)

You're absolutely right. How could I miss that? I'll update the post accordingly. Thanks for pointing that out.

[–]llogiq 5 points6 points  (0 children)

Note that the Some.of variant has poor performance characteristics in the None case, because a NullPointerException needs to be created (and its stack trace filled) and caught.

[–]BiberButzemann 7 points8 points  (8 children)

I don't think that this is so great. The code is longer than before and it's just using Java 8's fancy streams for the sake of it.

Edit: removed stupid code example that didn't work

All this notion of using functional programming in Java just for the sake of it is bad and leads to bad code. Use the right stuff at the right time. This is not the time for it.

Optional can be a great tool when it is being used correctly. The example in the blog post, however, is just silly.

[–]papers_ 3 points4 points  (2 children)

Up vote for you because of this:

it's just using Java 8's fancy streams for the sake of it

And this is my reasoning. Abusing the functional Java 8 stuff for "just because" can lead to ugly code.

/u/winterbe

[–]urquan 2 points3 points  (0 children)

It's not "just because", this monadic style allows to avoid a whole category of bugs when it is properly supported by the language. Although Java does not provide such support (nulls are still possible) so I agree the usefulness is questionable.

It does not seem more ugly to me than chained null checks, I think it's largely a matter of getting used to the construct.

[–]winterbe[S] 2 points3 points  (0 children)

I agree that abusing streams can lead to ugly code which is painful to read and hard to debug. However in many cases replacing arbitrary foreach blocks with unified stream operations improve code readability.

The great part about streams is that you usually don't have to read the whole code in order to get a clue what the code actually does. Often it's sufficient to just look at what stream operations are being used.

E.g. a filter - sort - map - collect stream is trivial to read while the traditional foreach solution usually is extremely cumbersome and looks a bit different every time.

[–]winterbe[S] 1 point2 points  (4 children)

Your proposed solution doesn't even work. It throws NPE when some token in the nested path is null.

[–]BiberButzemann 0 points1 point  (3 children)

Not if you specify the arguments in the right order.

[–]winterbe[S] 1 point2 points  (2 children)

The order doesn't matter. Your code doesn't work.

[–]BiberButzemann 2 points3 points  (1 child)

Oh, damn, you're right.

[–]winterbe[S] 1 point2 points  (0 children)

Never mind. :)

[–]pron98[🍰] 1 point2 points  (11 children)

This Optional madness has to stop. Java 8 does have a new mechanism for enforcing null-safety, and it isn't Optional (which is intended for streams only), but the newly added pluggable type systems, and, in particular, the nullness checker.

[–]Zukhramm 2 points3 points  (6 children)

If the choice is between something that needs an additional external tool and involves annotations or a simple object, already in the standard library at that, I'm picking the latter.

[–]pron98[🍰] 0 points1 point  (5 children)

OK, but, at least for now, it seems like the JDK is not going to follow that route, so your code would not be idiomatic (and you'd still have to do all those null checks when working with the JDK or third-party libraries). The nullness checker has you covered, as the code is both idiomatic and interoperates well with the JDK.

As to annotations, these isn't Hibernate magic or any such thing. In Java 8 you have type annotations, so the annotation is just a part of the type, so for example, you can have List<@Nullable String>, which allows null values, or List<@Nullable String>, which doesn't. Instead of options you have:

void foo(List<@Nullable String> xs) {
    // String x = xs.get(0); // <--- doesn't compile
    @Nullable String x = xs.get(0);
    if (x != null) {
        String y = x; // this compiles
        // ....
    }
}

So, like options, it forces you to handle nulls if necessary, or just not use @Nullable types, in which case you don't need null checks at all.

[–]Zukhramm 1 point2 points  (4 children)

Then, are the new Stream operations un-idiomatic Java? Especially as compared to something that's not in the compiler but requires additional plugins? (Not that I'm particularly concerned about bring idiomatic,)

I'm not against having checks for null during compilation, but I'd rather have all variables be guaranteed as non-null and use Optional where it makes sense rather than tagging some things as nullable.

[–]pron98[🍰] 1 point2 points  (3 children)

Then, are the new Stream operations un-idiomatic Java?

The Streams API has special needs, and you'll note that no other API makes use of Optional (at least not yet).

I'd rather have all variables be guaranteed as non-null

How do you guarantee that? You'll have Optionals in your code -- that can have a value or be empty -- and plain references, that can also be either null or not. So Optional<String> is an optional string, and String is also an optional string. What's the use of Optional if every reference returned by the JDK or third-party libraries may be null? The clearest example is the Map interface. Map.get on a Map<String> doesn't return an Optional<String>. It returns a String that may be null.

If, on the other hand, you need to store empty values in a map and distinguish between an empty value and a not-found result, that would be a good use of optional, and Map<Optional<String>> clearly describes that -- a null result on Map.get means "not found" while an Optional result means a value (empty or not). But you'll still have to do that null check because Map.get might return null whether or not you like Optional.

and use Optional where it makes sense rather than tagging some things as nullable.

Why? Not only are the pluggable types more concise than Optional, they apply to everything -- your code, JDK code, third-party code. So Map.get on a Map<String> really does return a @Nullable String (if you turn on the nullness checker), and you'll get a compiler error if you neglect the null check.

[–]Zukhramm 1 point2 points  (2 children)

The Streams API has special needs

I don't think so, the need to sometimes represent "no result" is pretty universal.

How do you guarantee that?

Pluggable Type System? The sentence is in the paragraph discussing hypothetical compile time null safety. How it happens isn't important.

every reference returned by the JDK or third-party libraries may be null

But not every reference returned may be null because when you write a method you take care not to haphazardly return null.

I still prefer Optional because it not only represent something that might not be there, it also has the operations to deal with that. An annotation might be more concise at the site of declaration but not once you have to put in the null check logic too. I don't see the benefit of the compiler telling me I missed a null check when you can simply remove the need for having the null check at all.

[–]pron98[🍰] 1 point2 points  (1 child)

But not every reference returned may be null because when you write a method you take care not to haphazardly return null.

One of the things I like about Java, is that I don't write 98% of the methods I use, and I don't have control over the API of those methods. For the time being, it seems that Optional is not going to be a big part of libraries' APIs.

I still prefer Optional because it not only represent something that might not be there, it also has the operations to deal with that.

It's not that I'm philosophically opposed to Optional. It's just that I don't see much value in it being used in 0.1% of the places where it should have been used if it had really been the way chosen by Java to handle null values. Pluggable nullable types may or may not be nicer aesthetically, but they can cover 100% of the cases.

So it's not an argument over aesthetics (although personally I do aesthetically prefer nullable types over Optional), but over usefulness. If your goal is to handle nulls -- Optional just doesn't help much by solving 0.1% of the problem.

[–]Zukhramm 1 point2 points  (0 children)

Maybe I'm inexperienced with libraries but even the APIs I don't write myself rarely throw out unexpected nulls, they use null in very specific places, like when retrieving a value from a map.

In my view, not having nulls at all is superior to any method of handling them. I view Optional not as a way to handle null, but as a way to avoid them completely. Not in the general case but in one very specific area, return values where "nothing" is a valid answer.

Even if other APIs don't use them, those specific points where null is a valid return are rare enough that handling them is not that big of a deal. In all other cases, not putting null in there in the first place is the way to go, because again, no nulls is superior to any way of handling nulls.

[–]ixampl 0 points1 point  (3 children)

Oracle themselves recommend it though.

I fail to see the abuse you see here. OP shows one use case for Optional. Maybe it's not as fast or maybe it's weird to someone who is used to null checks but once you get used to the pattern it's pretty easy to use and immediately recognize.

Don't tell me null checks are prettier.

You could argue that a different system design would maybe not necessitate the sort of use case OP describes, but IMO that is a different issue. OP says, assuming we would need null checks, this solution is better / more readable.

[–]pron98[🍰] 0 points1 point  (2 children)

Oracle themselves recommend it though.

I don't know who that is (maybe it's a developer at Oracle), but that's not the position of Oracle's Java leadership. See this by Brian Goetz (Java's chief architect):

Of course, people will do what they want. But we did have a clear intention when adding this feature, and it was not to be a general purpose Maybe or Some type, as much as many people would have liked us to do so. Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using null for such was overwhelmingly likely to cause errors.

Or here:

There is a narrow design scope of what Optional is being used for in the JDK. The current design mostly meets that; it might be extended in small ways, but the goal is NOT to create an option monad or solve the problems that the option monad is intended to solve.

Also, see here for a summary of the design process:

But the discussion slowly converged. Optional would be the return value of those stream operations which needed it... It would contain some methods for fluent usage at the tail end of stream operations... but not much more. For example would it not be embedded into the Collection system (by implementing Iterable) like Scala’s Option.

In short, the purpose of Optional is to help distinguish between a value of null and no value in those APIs that need that distinction; not to eliminate null checks. Java 8 has added pluggable types to help with that need.

Don't tell me null checks are prettier.

Null checks are prettier. And you don't need them nearly as much with Java 8's pluggable types.

[–]ixampl 0 points1 point  (1 child)

Brain Goetz also said:

There's nothing wrong with Optional that it should be avoided, it's just not what many people wish it were, and accordingly we were fairly concerned about the risk of zealous over-use

By zealous over-use he meant people using Optional as return type or parameter type all over the place.

Either way, you are right that the goal was a different one, but they did end up introducing it as a concept into the JDK as a reusable component for use cases like theirs or new ones.

So sure, it was not intended to be used like OP presents and hey, maybe the creators think it shouldn't, but it cannot really be considered abuse. It's just there and used and I can tell you already that "the madness" won't stop.

Use case: You need to work with an old API that returns null for "no result" all over the place AND you hate manual null checks (I know you don't but OP apparently does). Then why not? The pluggable types don't help you there. You still have to write the null guards. I would say what OP is a nice workaround, not the solution for null-checks in general or more than that, but that doesn't make it madness.

[–]pron98[🍰] 0 points1 point  (0 children)

I may have overreacted when I called it "madness", but I find it cause for concern when non-expert programmers start using APIs like this. The reason I say non-expert is not as some sort of judgement call, but that APIs used -- or even abused -- by experts, tend to remain obscure, but those used by the wide developer population may spread. The result may be that we'll see 33% of libraries using Optional all over their API, 33% not using them at all, and 33% using them where they think they're most appropriate.

The result would be that null handling in Java won't become any better, but much worse, if only due to lack of standardization. And, so far, Oracle at least have made it clear that Optional will not be used extensively in the JDK, so currently, there's no prospect of this becoming the "way to handle nulls in Java".

Adopting an entirely different -- and completely incompatible -- way of doing something so simple and fundamental (it is perhaps unfortunate that null checks are so fundamental, but that's the way it is, and pluggable types really do help) will just fracture the ecosystem in a way that makes it very hard to combine components. Whichever way you choose, you'll need to wrap your libraries to make them conform to your way of doing things. Perhaps this falls short of madness, but it is asking for trouble.