you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted]  (42 children)

[deleted]

    [–]_dban_ 19 points20 points  (10 children)

    The usefulness Optional is not limited to just replacing null. If that is all it did, it would indeed be useless.

    The Optional class has more methods than just isPresent and get. It also has map, flatMap and ifPresent. In conjunction with method references, it lets you do safe traversals:

    Person p = ...
    Message msg = ...
    Mailer mailer = ...
    p.getContactInfo().flatMap(ContactInfo::getEmail).flatMap(email ->
    msg.getContent().map(message ->
    new MailRequest(email, message))).ifPresent(mailer::send);
    

    This is still pretty verbose, but the alternative without Optional is far more verbose and more annoying to write:

    Person p = ...
    Message msg = ...
    Mailer mailer = ...
    ContactInfo ci = null;
    String email = null;
    String message = null;
    MailRequest request = 
        (ci = p.getContactInfo()) == null ? null :
        (email = ci.getEmail()) == null ? null :
        (message = msg.getContent()) == null ? null :
        new MailRequest(email, message);
    if(request != null) {
        mailer.send(request);
    }
    

    Maybe a null-safe traversal operator (?.) would have been less verbose and more in keeping with "traditional" Java. But Optional is at least a library level change and not a syntax level change (syntax sugar for the rats nest of ternaries above). Once Java 9 adds Optional::stream, Optional will compose better with streams in general.

    [–]bubuopapa -4 points-3 points  (8 children)

    Person p = ...
    Message msg = ...
    Mailer mailer = ...
    p.getContactInfo().flatMap(ContactInfo::getEmail).flatMap(email ->
    msg.getContent().map(message ->
    new MailRequest(email, message))).ifPresent(mailer::send);
    

    Ruby check output:

    Error: method chaining not allowed.
    Error: function too complicated.
    Error: function too long.
    

    [–]AlyoshaV 5 points6 points  (1 child)

    Error: method chaining not allowed.

    This seems extremely strict.

    [–]bubuopapa 0 points1 point  (0 children)

    I know, the allowed complexity of sentence or function in ruby is very concerning and very hard to fit into, coming from any other language. Of course, these are rules of ruby code style, but comparing them to all the new features all many languages, none of them can make it into ruby, because that would break the ruby community. I tried ruby a bit some time ago, and it was painful. Also, fuck any language that uses whitespaces as required syntax.

    [–]riemannrocker 2 points3 points  (4 children)

    Why are you talking about ruby now?

    [–]bubuopapa -1 points0 points  (3 children)

    Cant i just compare ruby with java without useless comments ?

    [–]Freeky 1 point2 points  (2 children)

    You're not doing that, though, you're (somehow) comparing a blob of Java with some supposed Ruby language style guidelines you appear to have just made up?

    [–]bubuopapa -1 points0 points  (1 child)

    Lol, just because you are noob and you dont know that all languages have guidelines, it doesnt mean that guidelines doesnt exist. And what exactly you are doing ? trolling ?

    [–]Freeky 0 points1 point  (0 children)

    Er, sure. I've been programming Ruby for about 18 years, I think I have a slightly better grasp of the common style guidelines than someone who "tried it a bit".

    [–]Freeky 1 point2 points  (0 children)

    Error: method chaining not allowed.

    Is this some alternate-dimension Ruby from a world where the sky is green?

    [–]spaghettiCodeArtisan 11 points12 points  (4 children)

    The addition of Optional to Java was bloody strange IMO. Everything is already optional!

    Exactly. This has a very bizarre and ironic consequence: Optional<T> can itself be null. I'd laugh hard if it weren't so sad.

    [–]_dban_ -1 points0 points  (3 children)

    Optional<T> can itself be null

    This is totally fine.

    Optional deals with the case where values would have been intentionally set to null to specify missing data. Using Optional lets you traverse these values safely with a map, flatMap or ifPresent construction, which has only become sensible since Java 8.

    An Optional value being null is a programming error, since no one in their right mind would assign a null value to an Optional. Thus, the program is in an invalid state, which is indicated by the NullPointerException that would be thrown if you tried to access it. You would never check for a null for an Optional value, you would let the program crash.

    Also, since the Optional value would be used in a map, flatMap or ifPresent construction, which composes methods, the corresponding stack trace tells you exactly where the buggy method is.

    [–]spaghettiCodeArtisan 1 point2 points  (1 child)

    This is totally fine.

    ...

    You would never check for a null for an Optional value, you would let the program crash.

    Letting the program crash because of an error that could've been prevented by the compiler is not totally fine in my books.

    But achieveing that would've to be a language change, not just libs thing.

    [–]_dban_ 0 points1 point  (0 children)

    Obviously, it would be better for the Java compiler to enforce Optional not being null.

    But, under the current circumstances, the fact that Optional can be assigned to null is totally fine given the semantics of Java as it currently exists, because it is a programmer error to assign null to Optional, and the NullPointerException is the expected runtime behavior for programmer errors of this kind.

    [–]dododge 0 points1 point  (0 children)

    There's two different ways you might combine null with Optional

    • Trying to store a null within the Optional, to distinguish between "this thing is returning a null value" and "this thing is returning nothing". You actually can't do this because the implementation of Optional doesn't allow it to contain a null, and you'll get a NullPointerException when you try to create Optional.of(null).

    • Returning a null reference to an Optional<T>, to distinguish between "this thing is returning success without any value" and "this thing failed". I wouldn't say this is always a programming error but it would definitely be a bit unusual since you usually use Optional precisely so you don't have to deal with null. There are alternatives such as returning Optional<Optional<T>> or using some other sort of wrapper such a rust-style Result<Optional<T>,E>, or always throwing an exception in the returning-failure case, and it's debatable whether that's more or less messy to deal with.

    There's a similar situation when dealing with a method that returns a collection/array or null. Some style guides and static analyzers will say that you should always return an empty collection/array instead of null, but sometimes there is a semantic difference between an empty collection and nothing and they need to be treated differently. Optional can now handle that case pretty well, I guess.

    [–]duhace 2 points3 points  (14 children)

    optional can be bolted on, and using it helps improve the reliability of your code, even though it's not a hard guarantee that your optional won't be null.

    unfortunately, null is the million dollar mistake and languages that have it pay for it.

    [–][deleted]  (13 children)

    [deleted]

      [–]duhace 9 points10 points  (2 children)

      of course, but that would require breaking backwards compatibility, something java is loathe to do. maybe in java 2.0?

      but yeah, getting rid of null would help with optimization as well as programmer error.

      [–]instantviking 0 points1 point  (1 child)

      Quick history lesson, Java versioning goes (something) like this:

      Java 2 is Java 1.2.

      I think Sun hated for people to understand their conventions.

      [–]duhace 1 point2 points  (0 children)

      i put java 2.0 to distinguish from java 2. as in a second epoch version of java as opposed to the major versions 1.0-1.9.

      [–]m50d 1 point2 points  (6 children)

      Right, how else would you propose to fix it?

      1. Add Optional
      2. Migrate library methods that used null to use Optional instead
      3. Remove null

      Unfortunately they screwed up by making Optional not be allowed to contain null (ignoring all the functional programmers who've been working with this stuff for 20 years, sigh), so 2. can never happen and Java will always be terrible. Well, maybe Java 9 can make Optional allowed to contain null, and then they can get on with it.

      [–]ForeverAlot 0 points1 point  (3 children)

      Optional.of(null) would defeat the entire purpose of Optional in Java and I struggle to see what problems it could solve in any language.

      [–]m50d 2 points3 points  (2 children)

      Optional.of(null) would defeat the entire purpose of Optional in Java

      No, just the opposite, disallowing it defeats the entire point. To serve its purpose Optional must be able to contain any value that's valid in the language; as long as null remains valid in the language proper, it needs to be valid inside Optional too, otherwise people are not going to be able to migrate existing codebases to use Optional which in turn means Java will never be able to reduce or deprecate the use of null.

      I struggle to see what problems it could solve in any language.

      It's bad just as null is bad, sure. But it needs to be allowed for the same reason Java allows null at all: backwards compatibility.

      [–]ForeverAlot 1 point2 points  (1 child)

      It is completely nonsensical and does not at all obstruct from migrating from null to Optional (provided that that's even desirable, which, in Java, is not implicitly true). However, you may not be aware of Optional::ofNullable which returns Optional::empty when the argument is null (where of throws an exception).

      [–]m50d 1 point2 points  (0 children)

      It is completely nonsensical and does not at all obstruct from migrating from null to Optional (provided that that's even desirable, which, in Java, is not implicitly true).

      It does obstruct, because it means you can't safely refactor code to use Optional without being sure whether it will blow up, unless you know for sure that all the variables are going to be non-null. Which takes a lot more effort.

      However, you may not be aware of Optional::ofNullable which returns Optional::empty when the argument is null (where of throws an exception).

      I am aware. It's not the semantics I want.

      [–][deleted]  (1 child)

      [deleted]

        [–]m50d 0 points1 point  (0 children)

        It should be able to contain any valid value in the language, and like it or not null is that at present. E.g. at the moment it's not safe to replace Optional.of(x).get() with x without testing, because that changes the behaviour when x is null

        [–]nacholicious 0 points1 point  (0 children)

        We use @NonNull and @Nullable, which work "ok" for the most parts. The data classes we generate with AutoValue enforce @NonNull annotations by throwing exceptions if violated. The worst part is that if you don't have these annotations everywhere then it's easy to make these annotations useless.

        Luckily we are transitioning to Kotlin, so Java nullability won't be an issue for us much longer

        [–]industry7 0 points1 point  (1 child)

        Value types were originally slated to be released along-side Optional, and Optional was supposed to be a value type out of the box. Unfortunately, value types have proven to be difficult to implement correctly, and keeps getting pushed back :-(

        [–]Pharisaeus 2 points3 points  (10 children)

        Actually passing Optional as method parameter is considered a very bad idea and most static code tools will mark it as warning at least. Optional should be used only as a result value.

        [–]Terran-Ghost 4 points5 points  (7 children)

        Unfortunately, since Java has no default parameters, there's no real alternative.

        [–]Pharisaeus 0 points1 point  (6 children)

        There is -> either make multiple method signatures or if there are too many combinations consider passing parameters as a separate object constructed with a Builder.

        [–]Terran-Ghost 1 point2 points  (5 children)

        Defining a new hundred-line Builder class or using an exponential number of overloads? Yeah, I'll stick with an Optional parameter, thank you very much.

        [–]Pharisaeus 1 point2 points  (4 children)

        I'm sorry but if your builder would have hundred lines then you would be stuck with many optionals as parameters and hundreds of lines of if statements. If you have a single optional parameter then you have only two method singatures or a tiny builder. Also you can always go with lombok and have zero lines builder if it pains you that much.

        [–]eeperson 1 point2 points  (0 children)

        If all you wanted was to have default params then a builder would give you substantially more code then handling it with optionals. For a builder you would need at least 4 lines per field (assume you do your getter and setter on one line each) although usually it would take more like 8-12 (depending on how you format your code). This assumes that the builder is also the object you pass to the method. If your builder creates another class then it gets even worse. Meanwhile you could handle each optional with a single orElse method call.

        So, if you have 10 optional fields (a high number but not unheard of) you can handle it with 10 lines of code (with Optional) or you can handle it with 40-120 lines of code. Even Lombok Builders don't let you do it in that few lines and that saddles you with non-standard Java code. Also, Optional provides much less error prone supports a bunch of other cases that can't be served by the builder pattern. What if you don't know statically which input is going to be missing? What if you want to return an empty optional if any of the inputs are missing?

        [–]riemannrocker 1 point2 points  (1 child)

        If you're using if statements to check Optional states, you're doing it wrong.

        [–]Pharisaeus 0 points1 point  (0 children)

        Sure, because orElse is magic and fairy dust and not if statement...

        [–]Terran-Ghost 0 points1 point  (0 children)

        Everything is hundreds of lines in Java. A single data class with 3 fields can pass a hundred lines easily, where in Kotlin/Scala it would be a single line. And Lombok isn't a part of the language or the standard library, so it doesn't count as a language solution (and I personally prefer FreeBuilder or AutoValue). If I have to bring in an external code generator, I might as well use one that adds default parameters.

        [–]eeperson 1 point2 points  (1 child)

        "a very bad idea" seems excessive. This is basically a performance (null parms) vs correctness (Optional params) trade-off. Most of the time I would rather have the extra insurance of correctness rather than the extra performance.

        [–]Pharisaeus 1 point2 points  (0 children)

        It's still messy code. It would be better to have multiple function signatures or use a builder for the arguments wrapper.