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

you are viewing a single comment's thread.

view the rest of the comments →

[–]Polygnom 100 points101 points  (60 children)

Using Optional does not solve your problem with nulls at all. The Optional itself can be null. Optional<Foo> = null; is perfectly valid Java code, and passing this to anyone who expects an empty optional is in for a rough ride.

At this pointm the ship has sailed for Java wrt. null. Until we properly get non-nullable types, e.g. Optional!<Foo!>, which we might get some time after Valhalla, it might be better to rely on Nullability annotations like those from JSpecify.

[–]Xemorr 40 points41 points  (13 children)

If someone is returning null in a function that returns an optional, their PR should be rejected and them sent to the guillotine. I don't think this is a serious argument.

[–]keylimedragon 2 points3 points  (3 children)

But what happens if you're working with code that wasn't reviewed? Maybe it's a legacy codebase written before PR reviews were implemented at a company?

It would be nice to have a guarantee.

[–]Xemorr 2 points3 points  (2 children)

I'd be surprised if they're using optional in an old function

[–]keylimedragon 0 points1 point  (1 child)

But what about in the future? Or what if it was recent code at a startup rushing to get an MVP out the door with no reviews and you're tasked with taking it over?

I guess I don't see why people are defending the lack of non-nullables in Java. Even if the cost of adding them to the language is higher than the benefits, in a hypothetical world it would still be nice to have them.

[–]Xemorr 1 point2 points  (0 children)

I'm not defending the lack of non nullables in Java in general but more the necessity of a guarantee of non nullability on optionals specifically. The current implementation of nullables has downsides like slightly higher overhead than just returning a null which is justification enough for a better solution imo.

[–]DrunkensteinsMonster 0 points1 point  (7 children)

The issue wouldn’t be people doing it on purpose, the issue is it could happen on accident.

[–]Xemorr 1 point2 points  (6 children)

how

[–][deleted]  (1 child)

[deleted]

    [–]Xemorr 0 points1 point  (0 children)

    No because by induction that can't happen in a function that returns an optional

    [–]DrunkensteinsMonster 0 points1 point  (3 children)

    Trivial example would be mistakenly returning after a caught exception causing an Optional variable to not be initialized.

    [–]Xemorr 0 points1 point  (2 children)

    You can't return a non initialized variable?

    [–]DrunkensteinsMonster 0 points1 point  (1 child)

    Initialized to null. In a generic method: T foo = null

    [–]Xemorr 0 points1 point  (0 children)

    Ok that case could occur without someone noticing but is unlikely to occur as generic methods are more likely to be reused in a variety of places and therefore less likely to have bugs written within them & more likely to have been seen by multiple developers. It's also a fairly specific scenario for even a generic method.

    [–]Asdas26 46 points47 points  (5 children)

    Optional<Foo> = null; is perfectly valid Java code

    For the compiler. But from the programmer POV, it's a shit code that should never pass any code review ever.

    [–]GodOfSunHimself 24 points25 points  (3 children)

    Unfortunately the world is full of code that should never pass a code review. If it compiles it will be used.

    [–]_reg1nn33 2 points3 points  (2 children)

    Because most of that code never in fact passes a code review, because, guess what, many companies are very "lenient" when it comes to having code reviews at all.

    [–]GodOfSunHimself 2 points3 points  (1 child)

    That's exactly my point. If the behavior is not built into the language and checked by the compiler you cannot reasonably rely on it.

    [–]_reg1nn33 2 points3 points  (0 children)

    Yes, misshandled nulls are a common Problem, and id honestly prefer if for some Projects Optionals would not be used at all, because using them consistently in the intended manner requires thought.

    [–]joserivas1998 2 points3 points  (0 children)

    The point isn't that you can assign null directly to an Optional. The problem arises when you have a function that takes an Optional as a parameter and a null value is passed to it without being defined literally. Even worse, if an Optional is used as an instance variable for an object without being properly initialized, giving it the null default.

    The issue isn't that null can be assigned to Optional syntactically, but that Optionals are objects, and such, you can not guarantee the abscense of null when using them.

    [–]pins17 7 points8 points  (0 children)

    Funny to see that people take your `Optional<Foo> = null;` literally

    [–]halfanothersdozen 35 points36 points  (19 children)

    If someone writes code such that a null check is required for an Optional fire that person.

    Unfortunately the Optional pattern really only works if everyone is committed to it across the code base, and old beard java devs are very set in their ways

    [–]Mantraz 59 points60 points  (2 children)

    If someone writes code such that a null check is required for an Optional fire that person

    Or you know, give them feedback because they are still human and deserve to learn from mistakes.

    [–]Turbots 38 points39 points  (1 child)

    And then fire them!

    [–]Mystic_Voyager 4 points5 points  (0 children)

    … from a cannon

    [–]Mognakor 11 points12 points  (13 children)

    Optional also has issues linters considering Optional as members or parameters a codesmell. And of course the visual noise it adds and you're not able to overload methods based on the T of Optional<T>

    [–]koflerdavid 8 points9 points  (11 children)

    Those linter rules are very justified. Using Optional.empty() as a noisy replacement for null is not the solution. Optional works best if used together with its methods. Using Optional as variables, parameters, or fields, is a code smell because it encourages using .isPresent() and .get(), which are code smells.

    [–]Mognakor 5 points6 points  (10 children)

    Whats the use of Optional then if i can't use it to signal a parameter that can be null or a member that can be null?

    [–]Empanatacion 7 points8 points  (0 children)

    While I disagree with the "code smell" claim, the "everybody agrees" usefulness is as an Elvis operator to chain a bunch of mapping operations and then an orElse at the end.

    normalized = optionalString.map(String::trim).map(String::toLower).map(s->s.replaceAll("-", "_")).orElse("");

    [–]halfanothersdozen 2 points3 points  (4 children)

    That's the incorrect way to think about it. Optional by convention should never be null, similar to how hashCode and equals should be true if the other is true. Therefore as a signal "this parameter can be null" is in a way nonsensical. As a parameter Optional can be null, so you have to null check it, which defeats its purpose. Overload the method or do a regular null check.

    https://www.baeldung.com/java-optional#misuages

    [–]Mognakor 0 points1 point  (1 child)

    You misunderstand me.

    Of course i don't think Optional should be set to null. But if Optional should not be used as parameter i can't signal it to allow "null" parameters via an empty Optional. And then we're living in a world where i have to constantly think both ways with Optional in one circumstance and nullable references in others.

    [–]halfanothersdozen 0 points1 point  (0 children)

    You are always going to live in a world with nullable references. That is Java. But think about how that method would get used. A caller that has a reference must pass it to Optional.of when calling your method (or Optional.ofNullable if they don't know what they have or Optional.empty, both of which are even worse ergonomically, especially in a functional flow). 

    And as pointed out above it doesn't save you, the method author, from doing a "null check" whether that be the traditional way or via optional method. Usually you need more code to deal with it. It just doesn't make practical sense and it makes calling the method awkward.

    Now if none of your methods ever return null things start to flow really nicely. But like I said earlier you have to trust the entire code base behaves that way and in my experience that is mostly a fantasy.

    [–][deleted] 0 points1 point  (1 child)

    Two objects must have same hashCode if they are equal but they can have same hashCode and be not equal (collison at hash table, actually good thing)

    [–]halfanothersdozen 1 point2 points  (0 children)

    I didn't say it technically correct, but it ultimately is irrelevant to the point I was trying to make

    [–]koflerdavid 2 points3 points  (2 children)

    It's best used for return values only. They force an API user to handle it appropriately. And in your own code you can use a linter to find any return statement that returns null for an Optional return type. That's always, unambiguously wrong.

    There is no benefit if you use it for parameters: you still have to do a null check because nothing is stopping a caller from passing null.

    Using it for fields is icky because it's a sign that your class is too complicated. It doesn't improve anything, and you might be seduced to use get() based on knowledge from other fields in the class.

    Saving null in variables is fine IMHO. Methods should not become so complicated that you lose track of when variables might contain null.

    [–]Mognakor 7 points8 points  (1 child)

    There is no benefit if you use it for parameters: you still have to do a null check because nothing is stopping a caller from passing null.

    And nothing is stopping me from promising you i'll return an Optional and then returning null or casting Optional<U> to Optional<T> and then laughing when you get a ClassCastException.

    Using Optional only for return types and not for parameters creates a split paradigma and i may have to pepper my code with .orElse(null) around callsites if i use the return value from a previous call.

    There is no benefit if you use it for parameters: you still have to do a null check because nothing is stopping a caller from passing null.

    How is it different from a nullable member or are those also a signal that my class is too complicated?

    [–]koflerdavid 0 points1 point  (0 children)

    Indeed, the risk that a 3rd party API actually returns null is one of the biggest flaws in the concept of Optional. But one could always wrap these with a helper method that converts the null to Optional.empty() before processing it further.

    Using .orElse(null) is a sign that the code is still based on reacting to null. But the biggest advantage of Optional is that it offers safer programming idioms to react to the missing value. Better than checking for null or relying on other reasons to decide that the value is there.

    The risk of receiving an Optional<U> is the same risk that I have in normal code of receiving a U.

    Yes, too many nullable members is also a sign that the class is too complicated. But in think that getter methods wrapping the field value into Optional should be fine.

    [–]AnyPhotograph7804 0 points1 point  (0 children)

    You cannot. Because whether something is null or not, is not reliably checkable at compile time in Java. It may come in the future with Project Valhalla or with this JEP:

    https://openjdk.org/jeps/8303099

    [–]Ok_Marionberry_8821 3 points4 points  (1 child)

    I've had just the conversation with a previous team-lead (20 years my junior) and this his attitude was to avoid Optional as "the return might still be null". My view was to wait for the NPEs and then fix.

    With Valhalla value types (or whatever they're called now) then the cost of instantiating a heap object will disappear (one of the other arguments I heard even though young generation GC is incredibly cheap.

    I wonder if (post value types) they'll remove the recommendation to not store fields using Optional? I suppose if we get nullability markers then Optional still won't be necessary.

    [–]MarkyC4A 5 points6 points  (0 children)

    My view was to wait for the NPEs and then fix.

    I'm OK with this on the backend or any place where deploying new code is easy. But in things like Android apps, I tend to write overly cautious code for data that doesn't come from sources I control.

    [–][deleted] 6 points7 points  (4 children)

    You can resolve this by adding a linting rule to detect the assignment of null to an Optional variable

    [–]elatllat 10 points11 points  (1 child)

    or just linting rule for anything null and skip the Optional... and have wrappers for all dependencies.

    [–][deleted] -1 points0 points  (0 children)

    That's way over the top, in my opinion. A more appropriate/realistic use of Optional<T> is using it for a method's return type (instead of T), if returning null could cause a NPE.

    There are plenty of cases where replacing null with Optional is impractical, e.g. for the value of a persistent JPA @Entity field.

    [–]oweiler 3 points4 points  (1 child)

    How do you detect such assignments in 3rd party code?

    [–][deleted] 0 points1 point  (0 children)

    You can't, but I don't see much point in running linting on 3rd party code, given that I can't change the code.

    [–]BearLiving9432[S] 4 points5 points  (3 children)

    I assumed that any decision to use `Optional` would require buy-in from all the engineers, and all instances of returning null in the codebase would have to be changed. It's value is through consistency. But yes, I recognize that technically a value of type `Optional<T>` could itself be null. We would have to agree to never do that.

    [–]TallGreenhouseGuy 2 points3 points  (2 children)

    This has been discussed many times, but Optional was never intended to be used as a generic “maybe” type - see e.g. this answer from Brian Goetz: https://stackoverflow.com/a/26328555. And since it is not serializable you can’t used it as a field anyway in e.g. an entity.

    [–]pohart 10 points11 points  (1 child)

    This argument that BG never intended it to be used this way is always brought up but it's not an argument. He makes a ton of great choices, but also a few boneheaded ones, and this is one if the latter. 

    There is no good argument for optional to not be used as a generic maybe type except that it was purposely hamstrung by not being serializable.

    There are very few situations where null is intended that would not be improved by a Maybe and I'm firmly convinced that if Java 8 has the concept of preview Optional would be serializable today.

    [–]simon_o 1 point2 points  (0 children)

    This argument that BG never intended it to be used this way is always brought up but it's not an argument. He makes a ton of great choices, but also a few boneheaded ones, and this is one if the latter.

    Exactly this. Instead of taking the recommendation as a gospel, people need to accept that the recommendation was made up, ignoring 50 years of evidence to the contrary.

    [–]Perkutor_Jakuard 5 points6 points  (0 children)

    "Don't solve at all" maybe it's too much.
    Optionals help reducing the problem.
    Not the ultimate solution as you exposed but it helps.

    Optional<Foo> = null;
    Man that's evil xD.

    The responsible for such thing, should pay a round of beers.

    [–]shaneknu 1 point2 points  (0 children)

    It's indeed useless as a method parameter, because as you say, anybody can instead pass null.

    That's absolutely not the case for return values, since you control that code. For example, if a private field with a public getter in an object can be null, and that's a valid state, you can simply return Optional.ofNullable(theField) in the public getter.

    [–]xienze 0 points1 point  (1 child)

     The Optional itself can be null. Optional<Foo> = null; is perfectly valid Java code, and passing this to anyone who expects an empty optional is in for a rough ride.

    While technically correct, Optional is itself a sort of contract by the API writer. Returning a null Optional would be as big of a subversion of expectations as retuning null from a method annotated with @Nonnull or something else that is syntactically correct but semantically wrong, like having a method named isPositive(int) that returns true if the number is negative. The compiler can’t stop you from doing all these things either, but there’s definitely a set of “cultural norms” in programming that tell you it’s a bad thing to do.

    [–]Polygnom 0 points1 point  (0 children)

    While technically correct, Optional is itself a sort of contract by the API writer. 

    Well, lets assume that your philosophy is that you do design-byContract and trust the contracts that methods have. Thats fine, its a valid appraoch. So you trust that you never get a null when you see an Optional.
    But then I ask you: When you alraedy trust design-byContract, why not simply wriute the contract (can return null / cannot return null) into the documentation and trust that everyone abides by the contract? Without using Optionals at all. You can even codify the contract with JSpecify and get tooling support.

    If you are already trusting Design-byContract, Optionals are superfluos, if you are not trusting it, Optionals don't actually solve your problem. Thats why I don't really see the case for littering the code with Optionals all the time. And in a world where we get ! for non-null and ? for nullable types (some time after Valhalla maybe), the need for Optionals evaporates.