What's the Point of Guava's Optional Class

What's the point of Guava's Optional class

Guava team member here.

Probably the single biggest disadvantage of null is that it's not obvious what it should mean in any given context: it doesn't have an illustrative name. It's not always obvious that null means "no value for this parameter" -- heck, as a return value, sometimes it means "error", or even "success" (!!), or simply "the correct answer is nothing". Optional is frequently the concept you actually mean when you make a variable nullable, but not always. When it isn't, we recommend that you write your own class, similar to Optional but with a different naming scheme, to make clear what you actually mean.

But I would say the biggest advantage of Optional isn't in readability: the advantage is its idiot-proof-ness. It forces you to actively think about the absent case if you want your program to compile at all, since you have to actively unwrap the Optional and address that case. Null makes it disturbingly easy to simply forget things, and though FindBugs helps, I don't think it addresses the issue nearly as well. This is especially relevant when you're returning values that may or may not be "present." You (and others) are far more likely to forget that other.method(a, b) could return a null value than you're likely to forget that a could be null when you're implementing other.method. Returning Optional makes it impossible for callers to forget that case, since they have to unwrap the object themselves.

For these reasons, we recommend that you use Optional as a return type for your methods, but not necessarily in your method arguments.

(This is totally cribbed, by the way, from the discussion here.)

Uses for Optional

The main design goal of Optional is to provide a means for a function returning a value to indicate the absence of a return value. See this discussion. This allows the caller to continue a chain of fluent method calls.

This most closely matches use case #1 in the OP's question. Although, absence of a value is a more precise formulation than null since something like IntStream.findFirst could never return null.


For use case #2, passing an optional argument to a method, this could be made to work, but it's rather clumsy. Suppose you have a method that takes a string followed by an optional second string. Accepting an Optional as the second arg would result in code like this:

foo("bar", Optional.of("baz"));
foo("bar", Optional.empty());

Even accepting null is nicer:

foo("bar", "baz");
foo("bar", null);

Probably the best is to have an overloaded method that accepts a single string argument and provides a default for the second:

foo("bar", "baz");
foo("bar");

This does have limitations, but it's much nicer than either of the above.

Use cases #3 and #4, having an Optional in a class field or in a data structure, is considered a misuse of the API. First, it goes against the main design goal of Optional as stated at the top. Second, it doesn't add any value.

There are three ways to deal with the absence of a value in an Optional: to provide a substitute value, to call a function to provide a substitute value, or to throw an exception. If you're storing into a field, you'd do this at initialization or assignment time. If you're adding values into a list, as the OP mentioned, you have the additional choice of simply not adding the value, thereby "flattening" out absent values.

I'm sure somebody could come up with some contrived cases where they really want to store an Optional in a field or a collection, but in general, it is best to avoid doing this.

Why did guava/java use possible.isPresent() as opposed to Optional.isPresent(possible)?

Because the correct way to react to the null value is by throwing a NullPointerException, and that's what you get when you call possible.isPresent() when possible == null.

Java as a language allows any value of a reference type to be null. It's up to the users of Java to not have null values when they don't want them, and to handle them correctly when they do want them. When they fail at that, a NullPointerException might be thrown.

Optional is not an alternative to writing == null. Optional is an alternative to using null. If you choose to not use null, and then use null anyway, you've created a bug.

What's the correct way to react to a programmer-introduced bug? Let's try your Optional.isPresent(value) idea in an example:

public abstract class BaseClass {
static boolean optionalIsPresent(Optional<?> possible) {
return (possible == null) ? false : possible.isPresent();
// return possible.isPresent();
}

public final String name;

public Optional<Integer> getID();

protected BaseClass() {
if (optionalIsPresent(getID())) {
name = "number " + getID().get();
} else {
name = "nameless";
}
}
}

public class DerivedClass extends BaseClass {
private final int id;

public DerivedClass(int id) {
this.id = id;
}

public Optional<Integer> getID() {
return Optional.of(id);
}
}

There is a programmer bug here, where they are trying to use a final field before it's set.

If optionalIsPresent(null) returns false, then the code above executes with no errors, and we have an object with behavior different from what we thought we specified: getID() is present, but name is "nameless". We get the same result as if we had never used Optional in the first place, and just used null and non-null values for "absent" and "present".

However, by using only absent() to represent absent(), our incorrect code throws a NullPointerException.

Guava Optional. How to use the correct

Guava contributor here...

Any or all of these things are fine, but some of them may be overkill.

Generally, as discussed in this StackOverflow answer, Optional is primarily used for two things: to make it clearer what you would've meant by null, and in method return values to make sure the caller takes care of the "absent" case (which it's easier to forget with null). We certainly don't advocate replacing every nullable value with an Optional everywhere in your code -- we certainly don't do that within Guava itself!

A lot of this will have to be your decision -- there's no universal rule, it's a relatively subjective judgement, and I don't have enough context to determine what I'd do in your place -- but based on what context you've provided, I'd consider making the methods return Optional, but probably wouldn't change any of the other fields or anything.

Java Optionals Confusion

They actually have been implemented in Java 8 so they actually haven't been "hiding" very long. But Google Guava had them for some time, and the Guava guide provides a very compelling analysis on why to use them.

https://code.google.com/p/guava-libraries/wiki/UsingAndAvoidingNullExplained

Nulls suck because they are difficult to determine if they are deliberately used for a design or not. Optional forces the developer to address the idea of an absent value and check for it first.

Not to mention, the Java 8 Optional has some very powerful lambda methods, like map(), flatMap(), and filter() which streamline a chain of transformative operations on a possibly empty value without risking a null pointer exception.

There really is no good reason to use null in your designs unless you're doing some performance-intensive process. Otherwise you are just subjecting yourself to NullPointerExceptions.

Is it a good practice to use Optional as an attribute in a class?

Java 8's Optional was mainly intended for return values from methods, and not for properties of Java classes, as described in Optional in Java SE 8:

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.

The key here is the focus on use as a return type. The class is definitively not intended for use as a property of a Java Bean. Witness to this is that Optional does not implement Serializable, which is generally necessary for widespread use as a property of an object.

Unwrapping a Guava Optional in a single expression

It's not there because we feel the anonymous class awkwardness of writing a Closure is more awkward and less readable -- at least in Java, not necessarily in Scala -- than the local variable and the if statement that you've already written.

That said, another alternative is

for (Foo x : someExpensiveOperation().asSet()) {
// do stuff with x
}

Note that asSet is necessary here -- Optional very deliberately does not itself implement Iterable.



Related Topics



Leave a reply



Submit