Optional<T> as a Record Parameter in Java
Asked Answered
S

1

9

The Optional<T> type introduced in Java 8 is mostly recommended to be used for return types and results. Hence, whenever I use it in a class field or a method parameter, I get a warning in IntelliJ:

Optional<?> used as type for field / parameter.

However, I do not get this warning when I use an Optional<T> as a record parameter in the canonical constructor:

public record AuthenticationResult(
    boolean isAuthenticated,
    Optional<User> user,
    Optional<String> authorizationHeader)
{ } 

Does the practice of not using Optional<T> types as parameters/fields not apply to record parameters because the field will always be accessed as a return value of the autogenerated getter method of the record?

Or is it because records are a new feature and this warning is yet to be implemented, and using an optional parameter in records has the same consequences as when used as a method parameter or field?

Stereobate answered 20/8, 2022 at 6:42 Comment(1)
Almost surely because the warning didn't get added when records got added.Fantastic
M
16

I would not attempt to explain why the current version of IntelliJ doesn't issue a warning for a record having optional fields (that's a question for developers of the IDE).

In this post, I'm addressing the question regarding recommended practices of using Optional, in particular with Java 16 Records.

Does the practice of not using Optional<T> types as parameters/fields not apply to record parameters

Firstly, Optional is not intended to be used as a field type, for that reason Optional doesn't implement Serializable (see). Secondly, records are data-carriers, their fields are final. Hence, if a record gets initialized with optionals that are empty, they would stay empty throughout all its time-span.

Usage of Optional

Here's a quote from the answer by @StuartMarks, Java and OpenJDK developer, regarding what Optional is meant to be used for:

The primary use of Optional is as follows:

Optional is intended to provide a limited mechanism for library method return types where there is a clear need to represent "no result," and where using null for that is overwhelmingly likely to cause errors.

The only valid usage for Optional is returning it from a method where it was originated. And the caller should immediately unpack the optional (API offers plenty of methods for that). But if you're passing the optional object around and storing it somewhere, then what you're doing isn't a good practice.

Optional is not meant for

Optional is not meant to be used:

  • As a field type;
  • As a type of the method parameter;
  • To be stored in a Collection;
  • Or utilized to perform null-checks. Substituting explicit null-check with Optional.ofNullable() is an antipattern.

Here's a quote from the answer by @Brian Goetz, Java Language Architect (Should Java 8 getters return optional type?):

Of course, people will do what they want...

For example, you probably should never use it for something that returns an array of results, or a list of results; instead return an empty array or list. You should almost never use it as a field of something or a method parameter.

Also, have a look at this answer by StuartMarks, here's a small quote:

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.

Records are Transparent carriers for Immutable data

An object with Optional fields forces the one who deals with it always take into consideration that the object obtained via a getter isn't a value, but potentially empty optional, which might throw NoSuchElementException if you blindly invoke get() on it.

Also, having fields of Optional type in a Record is in contradiction with the general concept of Records.

Here's a definition of Record from the JEP 395:

records, which are classes that act as transparent carriers for immutable data. Records can be thought of as nominal tuples.

A record with Optional fields is no longer transparent, because we're not able to get a value directly from it via accessor method.

Since record fields are immutable, it doesn't make sense to store potentially empty optionals as record properties, it almost the same as storing null-references. Because fields in a record can't be changed, an empty optional will remain empty, and some of your tuples might not contain useful data at all.

There's no advantage in passing around optionals, storing them inside records in order to find out at a later point in time that they don't contain the actual data.

Instead, you have to extract a value (if present) from an Optional object obtained from somewhere right on the spot before creating a record.

Moonraker answered 20/8, 2022 at 7:59 Comment(5)
It's absolutely valid for empty value to stay empty in a final field. In the same way It's absolutely ok for the null to stay null after assigned to a final field. This argument of staying empty just doesn't cut in.Corina
I see a good value in using Optional for a record in the case where you want to show / warn that record field value might be null. Of course this only makes sense if your records are such that all the other fields are never supposed to be null. I don't use generic records, I create specific ones for when I need to carry certain data and all the fields are always filled. Just my two centsFreestanding
Also, using Optional to make a multistage null check and return a default value at the end makes for a nice looking, readable code. I don't care what they were originally meant to be. Making canon out of language concepts is not helpful.Freestanding
Of course, naming record fields in a certain way or annotating them also works for warning that they might be null. That is a solution some people may like better.Freestanding
Here is an interesting article arguing that you can use Optional as a parameter or field: dzone.com/articles/optional-method-parameters The fact that they did not have this use in mind when designing Optional, doesn't mean that it doesn't have value in these cases.Donnie

© 2022 - 2024 — McMap. All rights reserved.