Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optional vs throwing an exception

Is it true that since Java 1.8 returning Optional object is more preferable than throwing an exception? Increasingly i see the code like this:

  public Optional<?> get(int i) {
        // do somtething
        Object result = ...
        Optional.ofNullable(result);
    }

Instead of this:

public Object get(int i) {
        if(i<0 || i>=size) {
            throw new IndexOutOfBoundsException("Index: " + i + ". Size: " + size);
        }
        // do somtething
        Object result = ...
        return result;
    }

Is it means that we need to forget old approach and use new? And where Optional is appropriate at all?

like image 207
Dmytro Avatar asked Mar 01 '15 16:03

Dmytro


People also ask

How do you throw an exception if Optional is empty?

Use the orElseThrow() method of Optional to get the contained value or throw an exception, if it hasn't been set. This is similar to calling get() , except that it allows for arbitrary exception types. The method takes a supplier that must return the exception to be thrown.

What is meant by throwing an exception?

The object, called an exception object, contains information about the error, including its type and the state of the program when the error occurred. Creating an exception object and handing it to the runtime system is called throwing an exception.

Does Optional catch exception?

Optional<T> is the generic immutable container which can hold either a single non-null T or nothing at all (empty). A method that conceptually returns T but under certain circumstances is unable to do so can return Optional<T> rather than throwing exception or returning null.

Why is Optional better for null handling?

In a nutshell, the Optional class includes methods to explicitly deal with the cases where a value is present or absent. However, the advantage compared to null references is that the Optional class forces you to think about the case when the value is not present.


3 Answers

The example you present is not an appropriate usage of Optional. An empty Optional represents a value that is absent for a reason which could not be predicted by the caller. It is the result of a legal invocation of a method.

The code you present as the "old idiom" performs validation of the input and throws an unchecked exception if the input was invalid. This behavior should remain unchanged even if you introduce Optional. The only difference would be that the return value of Object get(i) is possibly null whereas the return value of Optional<?> get(i) is never null because there is a special state of the Optional instance representing the absence of a value.

The advantage of methods which return Optional instead of a nullable value is the elimination of boilerplate code which must make a routine null-check before trying to do anything with the returned value. There are many more advantages to using Optional purely within a method. For example:

static Optional<Type> componentType(Type type) {
  return Optional.of(type)
                 .filter(t -> t instanceof ParameterizedType)
                 .map(t -> (ParameterizedType) t)
                 .filter(t -> t.getActualTypeArguments().length == 1)
                 .filter(t -> Optional.of(t.getRawType())
                                      .filter(rt -> rt instanceof Class)
                                      .map(rt -> (Class<?>) rt)
                                      .filter(Stream.class::isAssignableFrom)
                                      .isPresent())
                 .map(t -> t.getActualTypeArguments()[0]);

Here an important benefit is perfect scope control: the same name t is reused in each new scope for a variable of a type appropriate to that stage of processing. So, instead of being forced to have variables in scope after their useful life has expired, and to invent a new name for each following variable, with this idiom we have the precise minimum that we need to proceed.

Just for interest, you can implement equals entirely in terms of Optional:

@Override public boolean equals(Object obj) {
  return Optional.ofNullable(obj)
                 .filter(that -> that instanceof Test)
                 .map(that -> (Test)that)
                 .filter(that -> Objects.equals(this.s1, that.s1))
                 .filter(that -> Objects.equals(this.s2, that.s2))
                 .isPresent();
}

Although I find this idiom very clean and readable, it is not currently optimized enough to be recommended as a production-worthy choice. Future versions of Java may make this viable, though.

like image 147
Marko Topolnik Avatar answered Oct 17 '22 05:10

Marko Topolnik


It is possible to abuse exceptions, nulls, and optionals equally. In this particular case, I think you're probably abusing optional, because you're silently hiding a precondition violation and converting it into a normal return. On receipt of an empty optional from your code, the caller has no way of differentiating "the thing I was looking for wasn't there" and "I asked an invalid question."

Because Optional is new, there is also a tendency for it to be over-used; hopefully over time the right patterns will be internalized.

Optional is an example of the null object pattern; it provides a safe way to say "nothing was there" when "nothing there" is a reasonable expected outcome. (Returning an empty array or an empty collection are similar examples in those domains.) Whether you want to represent "nothing there" by null/optional vs an exception is generally a function of whether "nothing there" is a commonly expected situation, or whether it is exceptional. For example, no one wants Map.get to throw an exception if the mapping is not present; mapping-not-present is an expected, not exceptional, outcome. (If we had Optional in 1997, Map.get might have returned an Optional.)

I don't know where you heard the advice that Optional is preferable to exceptions, but whoever told you that was ill-informed. If you threw an exception before, you probably should still throw an exception; if you returned null before, you can consider returning Optional instead.

like image 43
Brian Goetz Avatar answered Oct 17 '22 05:10

Brian Goetz


In situations where errors might arise, a suitable datatype is Try.

Instead of using the abstractions 'present' or 'empty', a Try uses the abstractions 'failure' or 'success'.

As Try is not provided by Java 8 out of the box, it is necessary to use some 3. party library. (Maybe we will see it added in Java 9?)

Try for Java

like image 5
mariatsji Avatar answered Oct 17 '22 06:10

mariatsji