Oh, those coding styles are to be taken with a bit of salt.
- (+) Passing an Optional result to another method, without any semantic analysis; leaving that to the method, is quite alright.
- (-) Using Optional parameters causing conditional logic inside the methods is literally contra-productive.
- (-) Needing to pack an argument in an Optional, is suboptimal for the compiler, and does an unnecessary wrapping.
- (-) In comparison to nullable parameters Optional is more costly.
- (-) The risk of someone passing the Optional as null in actual parameters.
In general: Optional unifies two states, which have to be unraveled. Hence better suited for result than input, for the complexity of the data flow.
Answer from Joop Eggen on Stack OverflowOh, those coding styles are to be taken with a bit of salt.
- (+) Passing an Optional result to another method, without any semantic analysis; leaving that to the method, is quite alright.
- (-) Using Optional parameters causing conditional logic inside the methods is literally contra-productive.
- (-) Needing to pack an argument in an Optional, is suboptimal for the compiler, and does an unnecessary wrapping.
- (-) In comparison to nullable parameters Optional is more costly.
- (-) The risk of someone passing the Optional as null in actual parameters.
In general: Optional unifies two states, which have to be unraveled. Hence better suited for result than input, for the complexity of the data flow.
The best post I've seen on the topic was written by Daniel Olszewski:
Although it might be tempting to consider Optional for not mandatory method parameters, such a solution pale in comparison with other possible alternatives. To illustrate the problem, examine the following constructor declaration:
Copypublic SystemMessage(String title, String content, Optional<Attachment> attachment) { // assigning field values }At first glance it may look as a right design decision. After all, we explicitly marked the attachment parameter as optional. However, as for calling the constructor, client code can become a little bit clumsy.
CopySystemMessage withoutAttachment = new SystemMessage("title", "content", Optional.empty()); Attachment attachment = new Attachment(); SystemMessage withAttachment = new SystemMessage("title", "content", Optional.ofNullable(attachment));Instead of providing clarity, the factory methods of the Optional class only distract the reader. Note there’s only one optional parameter, but imagine having two or three. Uncle Bob definitely wouldn’t be proud of such code 😉
When a method can accept optional parameters, it’s preferable to adopt the well-proven approach and design such case using method overloading. In the example of the SystemMessage class, declaring two separate constructors are superior to using Optional.
Copypublic SystemMessage(String title, String content) { this(title, content, null); } public SystemMessage(String title, String content, Attachment attachment) { // assigning field values }That change makes client code much simpler and easier to read.
CopySystemMessage withoutAttachment = new SystemMessage("title", "content"); Attachment attachment = new Attachment(); SystemMessage withAttachment = new SystemMessage("title", "content", attachment);
I like using Optional<> to return values that may be null but today I saw people using them to accept parameters that may be null, if the parameter wasn't an Optional<> then it couldn't be null. I personally don't like doing that but I'd like to hear some opinions.
They were doing things like
double getStartingBalance(@NotNull Optional<UUID> user) { ... }
In my opinion, Optional<> is used to force the developer to handle null values. If my method is called and may return null, I'll return an Optional<> to make sure that null is handled but if I design the method and define that something cannot be null I can just use Objects#requereNonNull or if it can be null a simple check does the job. Also having to box values in optionals and having to use Optional.empty() for nulls to call a method is messy and makes the call harder to read.
So what are your opinions on using optionals as parameters?
Videos
I've been using Optional heavily in my return types to avoid null checks, which feels clean. However, I've recently seen debates about whether Optional should be used as a method argument (e.g., public void doSomething(Optional<String> value)).
Some say it's better to just overload the method or pass null, while others say it makes the API clearer.
As a beginner dev trying to write cleaner APIs, what is the industry standard here? Do you strictly keep Optional for return types only?
Whether something is "OK to use" depends on intent, team coding practices, common programming idioms, and how strictly you want to adhere to API guidelines.
First, the facts about Optional<T>:
A container object which may or may not contain a non-null value.
...
API Note:
Optional is primarily intended for use as a method return type where there is a clear need to represent "no result," and where using null is likely to cause errors. A variable whose type is Optional should never itself be null; it should always point to an Optional instance.
Source: Oracle Java 11 JDK documentation (emphasis, mine)
Not a lot of concrete guidance here beyond the recommendation that Optional<T> be used as a return type. In a strict sense, you are not using this as a return type, therefore the answer is "no, this is not OK." On the other hand, you are using it as the type for a field which may contain null. So you seem to satisfy the first part.
But it works.
That's why it's important to remember these are guidelines. The mechanics of whether you can use something are a different discussion, and hence, your question. Instead, I like to see how these optional fields are being used to ensure a more suitable OO design doesn't already exist.
If I had to look at a codebase that was peppered with field.ifPresent(foo -> foo.method()) every time field was used, I would start to wonder if we have a design problem with field rather than it's type. Even in cases where you have field.isPresentOrElse(foo -> foo.method(), () -> something else), I still think we are missing something. Frankly, the constant calls to the Optional<T> methods as a round-about way to avoid NullPointerExceptions makes me think the Null Object Pattern might be a better choice here. Every method in the "null object" can be a no-op. Getters can return pre-canned values (whatever the "or else" part of Optional.ifPresentOrElse() would have returned).
I don' like optional dependencies because they can be null, so now I need to incessantly check for null, or I need to use some other non-idiomatic construct like Optional<T> with a bunch of pass-through method calls in lieu of null-checks.
Code is much cleaner if you can simply call field.method() rather than field.ifPresent(foo -> foo.method()).
Some additional notes:
The guidance in this answer applies best to situations where the
Tis a complex type. WhenTis a string, boolean, or numeric type, then the "optional" part of anOptional<T>becomes meaningful. AnOptional<boolean>can be true, false, or not present, which your application could interpret as "not specified." In that case, document this fact on the field.- In the case of a String, consider defaulting to an empty string instead. No need for null if you have proper encapsulation. A constructor or setter can enforce the "empty string instead of null" constraint.
When
Tis a complex type, consider other, more straight-forward designs first before resorting to anOptional<T>, if for no other reason than the Principle of Least Astonishment.Choose code that is easy to read first. In situations like this, I like to write the same code multiple ways. Consider writing it when checking for nulls, using another more suitable design pattern (like Null Object Pattern), and using an optional. Same code. 3 different styles. Which one do you like?
- And then present the same 3 styles to a colleague to see which one they like.
In the end, you get working code either way. Personal taste, other object-oriented design practices and team habits play a bigger role in this decision than API guidance.
Optional is now intended to be used as a Monad. Java designers effectively admitted that by adding Optional.stream() method.
Ignore earlier discussions about:
- serialization. Serialization should not be used in new code
- library interfaces. They are not different from your own.
- method argument. Using Optional as argument in much more idiomatic than doing a conditional dispatch over method overloads.
Surprisingly, important reasons not to use an Optional as a field are:
- memory consumption - it increases the size of your field up to 4 times.
- performance - it introduces additional memory indirection
These are only applicable to long-lived numerous objects (example: gamedev).
One reason is that conceptually, firstName, middleName, and lastName are logically one argument, not three. They're all parts of a bigger whole, and one can imagine that they're almost always passed together. In functional languages they'd likely be passed as a tuple or record; Java lacks those, so they'd probably be aggregated into a Name class. Note that if you needed to compose functions that take and return full names, you wouldn't be able to without aggregating them - you can only return one value, after all.
Maybe it just doesn't come up very often that a value is optional and also not part of a bigger whole. If the function requires a certain value to do its work, then that function shouldn't be accepting an Optional. If you need to chain operations that might not return a value, aborting with Nothing if any function along the way fails, you can chain calls to flatMap, and if you want to fail with an exception you can use get() at any step of the way or at the end of the chain.
If a value is truly optional and a function does two different things based on its presence or absence, that's a code smell unless the function is a wrapper of two smaller functions that do one thing each, and that particular combination of decisions is very common.
I don't think it's so much that it's wrong as it is a relatively uncommon use case.
There aren't very many languages that don't support default arguments, so this is not a common usage. I personally think your usage isn't terrible, but it's not going to be idiomatic Java. Java very intentionally doesn't have default arguments, to force the use of overloading instead, which has the advantage of the compiler checking your arguments instead of requiring if statements inside the function.
In other words, you're going to be making two different versions of the query: one with a middle name and one without. If you can do that in a concise way, using an option is a good idea. If it's verbose enough that you'd want it in two separate functions anyway, you may as well use the overloading and make it idiomatic.