Monday, January 29, 2018

Considerations When Returning Java 8's Optional from a Method

The Optional class introduced with Java 8 has been one of the most controversial features introduced by that version of the language. Although I like more things about this new Java class than I dislike, there are a few things to consider when employing it as a return type in Java methods. I discuss some of them in this post, but don't discuss the controversy about whether Optional should be limited to use as a return type. I also assume that Optional is only used as a return type when it's expected that there are cases where that method should have no value to return. Finally, these observations apply to other types and to direct use of null in Java as well, but Optional emphasizes and concretely illustrates these observations.

Single Versus Multiple Returns

There has been a debate ("religious war") in the general software development community and in the Java development community specifically for some time about whether methods should be written to only return once (not counting throwing exceptions in this discussion). On one side, Yegor Bugayenko argues that "many return statements are a bad idea in OOP", Tom Dalling argues that "having one exit point (return) from a function is a good thing", and many have argued that multiple return statements often indicate need of that method to be refactored. On the other side, Bruce Eckel argues that multiple return statements can make the code "clearer", Taylor Gautier argues that the "maxim" "A method should have one and only one exit point" "couldn't be more wrong", Peter Ritchie argues that strict adherence to single-exit can lead to "less readable" code today in "in Object-Oriented languages", and Mark Levison outlines "some reasons I dislike the single exit argument."

In the post "Multiple Return Statements," Nicolai Parlog writes about the history of and considerations to be made related to the idea of a method returning only once. He includes a section "Situations For Multiple Returns Statements" in which he outlines "several kinds of situations in which a method can profit from multiple return statements." My best guess is that many developers feel the way I do, which is that "it depends" when deciding whether a particular method should have only one return statement or should have more than one return statement.

As I've begun using Java 8's Optional more frequently for my methods' return types, I'm finding that the use of Optional as a return type is another consideration to be made when deciding whether to return once or multiple times from a method.

When declaring that a Java method returns an Optional it's important to fully understand that this does not preclude the developer who writes this method from returning null. The returned Optional is a reference type and, like any reference type, can be null. It is paramount that a developer writing a method that returns Optional should NEVER have that method return null [Optional.empty() should generally be returned instead]. I'm going to reiterate this point with two quotations:

  • Highlighted sentence from Item #55 in Effective Java, Third Edition: "Never return a null value from an Optional-returning method."
  • Stuart Marks's #1 rule for using Optional, "Never, ever, use null for an Optional variable or return value."

One of the arguments against multiple return statements in a method is that it makes it more difficult to recognize what is returned in each case (to find all the different possible return scenarios). The use of Optional as a return type is a specific example illustrating this. One would want to make sure that one's method does not return null in some cases and an Optional instance in other cases. The compiler certainly won't care which is returned in each case.

One approach for dealing with this is to only return from a method once and then the developer writing the code and the developer reviewing the code can easily ensure that null is not returned. These developers would need to only look for an Optional.of(T) call, an Optional.ofNullable(T) call, or an Optional.empty() call.

Work with Local Variable of Underlying Data Type in Method

This approach at avoiding the accidental return of null instead of an empty Optional works best when the Optional is instantiated at the point of return. In other words, I have found it better to work with the type wrapped by the Optional throughout the method and then place it in the Optional at the last possible moment. The next code listing provides ridiculously trivial examples of this.

Examples of Declaring Local Variable that is Ultimately Returned as Optional

/**
 * Provides the middle name if it exists.
 *
 * @return Middle name if it exists or empty if it doesn't exist.
 */
public Optional<String> determineMiddleName1()
{
   String middleName;
   // Do whatever logic is necessary
   return Optional.ofNullable(middleName);
}

/**
 * Provides the middle name if it exists.
 *
 * @return Middle name if it exists or empty if it doesn't exist.
 */
public Optional<String> determineMiddleName2()
{
   Optional<String> middleName;
   // Do whatever logic is necessary
   return middleName;
}

In the above code, the determineMiddleName1() method works with the local variable of the underlying type. This is typically easier to set/populate than Optional and the use of Optional.isNullable() at the ends ensures that even null middleName will be returned as an "empty" Optional instead of null.

The determineMiddleName2() method in the above code declares its local variable that will ultimately be returned as an Optional<String> and then returns that reference at the end of the method.

Avoid "Default" Initialization of Local Variable

As method determineMiddleName2() is written above, the compiler will help ensure that the local variable "middleName" is set to something (even if that "something" is null), but had the developer chosen to initialize that "middleName" variable to null to begin with, the compiler would have no issue with it. If the local variable needs to be initialized for some reason, it would be better to initialize it to Optional.empty() instead of null. Had the developer chosen to initialize that variable with Optional.empty(), it's still possible for that second example to "reset" the local variable to null later in the method.

This discussion leads me to three opinionated observations regarding use of Optional as a method return type in Java.

  1. The influence of single return or multiple returns on the possibility of returning null accidentally instead of an "empty" or other non-null Optional reference should be considered when deciding on whether a single return or multiple returns makes most sense.
  2. It will often be easier to ensure return of an Optional reference rather than return of a null by working on the the underlying type throughout the method and only instantiating the returned Optional at the latest moment (typically at its return).
  3. A local variable of type Optional that is ultimately to be returned from a method should NEVER be initially assigned to null even if it is "known" that it will be re-set appropriately. It may be best to not define it at all so that the compiler will ensure it needs to be set in each "branch" of the code flow. At the very least, if it is to be initialized, that local variable of type Optional should be initialized to Optional.empty() instead of to null.

These observations can be combined. For example, when it's determined that a method should have multiple returns (such as to implement guard clauses), one could return appropriate non-null Optional references at the point of each return and not initialize the local variable until needed (after getting through the guards). This is illustrated with the next ridiculously contrived example.

Example of Avoid Return of Null with Multiple Return Statements

public Optional<String> getGuardedData(final String input)
{
   if (input == null)
   {
      return Optional.empty();
   }

   String data;
   // Do whatever logic is necessary
   return Optional.ofNullable(data);
}

I have found the Optional class, when used properly as a method return type, can make the client's code more readable because of its greater fluency. However, to achieve its maximum value, Optional must be applied with discipline such that clients of the code can expect the returned Optional to never be null. This post has looked at a few considerations that can be made to help ensure that null is never returned from a method that advertises itself as returning Optional. Without trust that the method will never return null, use of Optional as a return type only makes things worse because it forces the client to first check for a non-null Optional before invoking one of the methods on that Optional. That makes the calling code less fluent.

3 comments:

S G said...

Newline-braces in Java code removes any credability immediately.

Unknown said...

@SG You mean credibility? ;)

Keith said...

@SG - your decision to comment on something as trivial as newline-braces instead of any considered discussion of the article removes any credibility that you have!