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:
Answer from Jesper on Stack OverflowOf 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
MaybeorSometype, 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 usingnullfor 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
Optionaldoes not implementSerializable, which is generally necessary for widespread use as a property of an object.
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
MaybeorSometype, 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 usingnullfor 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
Optionaldoes not implementSerializable, which is generally necessary for widespread use as a property of an object.
I think it is a theoretical question.
The notion of optional values was brought from functional languages world. Those languages usually also support pattern matching on language level and allow you to pattern match on the optional value.
In functional languages function calls usually return an optional value that other code could pattern match on.
I have never seen passing an optional as argument, but that does not mean it is a bad think. It looks weird though.
Videos
Hello everyone!
My question is basically the title. I wonder why you are discouraged to use Optional and its derivatives as the type of member variables. When using an IDE like IntelliJ or reading blogs on the internet, everyone discourages you of using Optional as a member variable. From what I can see, there are mostly two reasons mentioned for this:
-
The first argument brought up is, that Optional is not Serializable. So an Object which has a member of type Optional, is not Serializable as well. At least you can't simply make it Serializable using the default implementation.
I understand why this could be a problem. You can't just simply implement the Serializable interface and are done. But as always you could simply override the default serialization process or choose not to serialize the Optional field by marking it as transient. The ArrayList implementation for example does the same as its contents aren't guaranteed to be Serializable. In my opinion, this argument is somewhat valid, but its not a reason to actively discourage the use of Optional. Especially since serialization in Java does have much more pitfalls.
2. The other argument I see, is that Optional was not designed to be used in this way. Apparently Optional is only "a limited mechanism to represent a method may have no result".
But in this case, I don't see a reason, why it should only be limited to the return type of methods. By using Optional as the type of a member variable, you can also communicate, that this field is not always present and have much less trouble with null values and checking for them.
I could see, that it's "more the Java way", to use null whenever some value is not present. But due to the way Java works, this is only possible for non-primitive member variables. If you were to use OptionalInt or OptionalLong, there is no good primitive alternative. In some cases, you could communicate a missing value using 0 or negative numbers, but perhaps this could be a valid value as well, making it even harder to check, whether the value is present. In this cases you could either use the boxed variant of int and long or you could use some magic constants to communicate a missing values in hope, the chosen value will never be a real result. Using the boxed variant of primitives seems like the better alternative here, but it is not optimal, since boxing not only costs performance, it will also give you confusing error messages, if Java tries to unbox a null value.
This is my point of view on this topic. Feel free to add you own opinion or perhaps resources, that explain why using Optional is a bad idea for member variables. I really hope to understand, why this decision was made in Java.
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).
You can provide a default value for the attribute:
@Target(value={ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotation {
Class<?> myType() default Object.class;
}
Found it. It can't be optional, but a default can be declared like this:
@Target(value={ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotation {
Class<?> myType() default String.class;
}
If no default can make sense as "empty" value then that is a problem.