Uses for Optional

前端 未结 14 1410
眼角桃花
眼角桃花 2020-11-22 01:01

Having been using Java 8 now for 6+ months or so, I\'m pretty happy with the new API changes. One area I\'m still not confident in is when to use Optional. I se

相关标签:
14条回答
  • 2020-11-22 01:13

    An Optional has similar semantics to an unmodifiable instance of the Iterator design pattern:

    • it might or might not refer to an object (as given by isPresent())
    • it can be dereferenced (using get()) if it does refer to an object
    • but it can not be advanced to the next position in the sequence (it has no next() method).

    Therefore consider returning or passing an Optional in contexts where you might previously have considered using a Java Iterator.

    0 讨论(0)
  • 2020-11-22 01:18

    Optional class lets you avoid to use null and provide a better alternative:

    • This encourages the developer to make checks for presence in order to avoid uncaught NullPointerException's.

    • API becomes better documented because it's possible to see, where to expect the values which can be absent.

    Optional provides convenient API for further work with the object: isPresent(); get(); orElse(); orElseGet(); orElseThrow(); map(); filter(); flatmap().

    In addition, many frameworks actively use this data type and return it from their API.

    0 讨论(0)
  • 2020-11-22 01:22

    Here is an interesting usage (I believe) for... Tests.

    I intend to heavily test one of my projects and I therefore build assertions; only there are things I have to verify and others I don't.

    I therefore build things to assert and use an assert to verify them, like this:

    public final class NodeDescriptor<V>
    {
        private final Optional<String> label;
        private final List<NodeDescriptor<V>> children;
    
        private NodeDescriptor(final Builder<V> builder)
        {
            label = Optional.fromNullable(builder.label);
            final ImmutableList.Builder<NodeDescriptor<V>> listBuilder
                = ImmutableList.builder();
            for (final Builder<V> element: builder.children)
                listBuilder.add(element.build());
            children = listBuilder.build();
        }
    
        public static <E> Builder<E> newBuilder()
        {
            return new Builder<E>();
        }
    
        public void verify(@Nonnull final Node<V> node)
        {
            final NodeAssert<V> nodeAssert = new NodeAssert<V>(node);
            nodeAssert.hasLabel(label);
        }
    
        public static final class Builder<V>
        {
            private String label;
            private final List<Builder<V>> children = Lists.newArrayList();
    
            private Builder()
            {
            }
    
            public Builder<V> withLabel(@Nonnull final String label)
            {
                this.label = Preconditions.checkNotNull(label);
                return this;
            }
    
            public Builder<V> withChildNode(@Nonnull final Builder<V> child)
            {
                Preconditions.checkNotNull(child);
                children.add(child);
                return this;
            }
    
            public NodeDescriptor<V> build()
            {
                return new NodeDescriptor<V>(this);
            }
        }
    }
    

    In the NodeAssert class, I do this:

    public final class NodeAssert<V>
        extends AbstractAssert<NodeAssert<V>, Node<V>>
    {
        NodeAssert(final Node<V> actual)
        {
            super(Preconditions.checkNotNull(actual), NodeAssert.class);
        }
    
        private NodeAssert<V> hasLabel(final String label)
        {
            final String thisLabel = actual.getLabel();
            assertThat(thisLabel).overridingErrorMessage(
                "node's label is null! I didn't expect it to be"
            ).isNotNull();
            assertThat(thisLabel).overridingErrorMessage(
                "node's label is not what was expected!\n"
                + "Expected: '%s'\nActual  : '%s'\n", label, thisLabel
            ).isEqualTo(label);
            return this;
        }
    
        NodeAssert<V> hasLabel(@Nonnull final Optional<String> label)
        {
            return label.isPresent() ? hasLabel(label.get()) : this;
        }
    }
    

    Which means the assert really only triggers if I want to check the label!

    0 讨论(0)
  • 2020-11-22 01:23

    1 - As a public method return type when the method could return null:

    Here is a good article that shows usefulness of usecase #1. There this code

    ...
    if (user != null) {
        Address address = user.getAddress();
        if (address != null) {
            Country country = address.getCountry();
            if (country != null) {
                String isocode = country.getIsocode();
                isocode = isocode.toUpperCase();
            }
        }
    }
    ...
    

    is transformed to this

    String result = Optional.ofNullable(user)
      .flatMap(User::getAddress)
      .flatMap(Address::getCountry)
      .map(Country::getIsocode)
      .orElse("default");
    

    by using Optional as a return value of respective getter methods.

    0 讨论(0)
  • 2020-11-22 01:23

    In java, just don't use them unless you are addicted to functional programming.

    They have no place as method arguments (I promess someone one day will pass you a null optional, not just an optional that is empty).

    They make sense for return values but they invite the client class to keep on stretching the behavior-building chain.

    FP and chains have little place in an imperative language like java because it makes it very hard to debug, not just to read. When you step to the line, you can't know the state nor intent of the program; you have to step into to figure it out (into code that often isn't yours and many stack frames deep despite step filters) and you have to add lots of breakpoints down to make sure it can stop in the code/lambda you added, instead of simply walking the if/else/call trivial lines.

    If you want functional programming, pick something else than java and hope you have the tools for debugging that.

    0 讨论(0)
  • 2020-11-22 01:24

    I'm late to the game but for what it's worth, I want to add my 2 Cents. They go against the design goal of Optional, which is well summarized by Stuart Marks's answer, but I'm still convinced of their validity (obviously).

    Use Optional Everywhere

    In General

    I wrote an entire blog post about using Optional but it basically comes down to this:

    • design your classes to avoid optionality wherever feasibly possible
    • in all remaining cases, the default should be to use Optional instead of null
    • possibly make exceptions for:
      • local variables
      • return values and arguments to private methods
      • performance critical code blocks (no guesses, use a profiler)

    The first two exceptions can reduce the perceived overhead of wrapping and unwrapping references in Optional. They are chosen such that a null can never legally pass a boundary from one instance into another.

    Note that this will almost never allow Optionals in collections which is almost as bad as nulls. Just don't do it. ;)

    Regarding your questions

    1. Yes.
    2. If overloading is no option, yes.
    3. If other approaches (subclassing, decorating, ...) are no option, yes.
    4. Please no!

    Advantages

    Doing this reduces the presence of nulls in your code base, although it does not eradicate them. But that is not even the main point. There are other important advantages:

    Clarifies Intent

    Using Optional clearly expresses that the variable is, well, optional. Any reader of your code or consumer of your API will be beaten over the head with the fact that there might be nothing there and that a check is necessary before accessing the value.

    Removes Uncertainty

    Without Optional the meaning of a null occurrence is unclear. It could be a legal representation of a state (see Map.get) or an implementation error like a missing or failed initialization.

    This changes dramatically with the persistent use of Optional. Here, already the occurrence of null signifies the presence of a bug. (Because if the value were allowed to be missing, an Optional would have been used.) This makes debugging a null pointer exception much easier as the question of the meaning of this null is already answered.

    More Null Checks

    Now that nothing can be null anymore, this can be enforced everywhere. Whether with annotations, assertions or plain checks, you never have to think about whether this argument or that return type can be null. It can't!

    Disadvantages

    Of course, there is no silver bullet...

    Performance

    Wrapping values (especially primitives) into an extra instance can degrade performance. In tight loops this might become noticeable or even worse.

    Note that the compiler might be able to circumvent the extra reference for short lived lifetimes of Optionals. In Java 10 value types might further reduce or remove the penalty.

    Serialization

    Optional is not serializable but a workaround is not overly complicated.

    Invariance

    Due to the invariance of generic types in Java, certain operations become cumbersome when the actual value type is pushed into a generic type argument. An example is given here (see "Parametric polymorphism").

    0 讨论(0)
提交回复
热议问题