Re: Records — Dealing with nullable values
Ben Evans
benjamin.john.evans at gmail.com
Sun Dec 22 12:17:34 UTC 2019
This is also a good opportunity to examine your domain and ask
yourself the question of why you need to record the gender of a person
at all.
Spoiler: In an overwhelming number of cases, you don't.
This has been a Party Political Broadcast on behalf of the Stop
Recording Data Points That You're Never Going To Use or Touch Again
Party.
Cheers,
Ben
On Sun, 15 Dec 2019 at 03:21, Tagir Valeev <amaembo at gmail.com> wrote:
>
> > But if you really want to treat "no
> > gender" as a valid point in the domain, then just make a record with an
> > Optional<Gender> component, and push the `Optional.ofNullable()` to the
> > caller (or, provide an overloaded constructor that does it for them.)
> >
>
> A better solution, to my opinion, is to encode an unspecified gender as one
> of the values within the Gender type and make it non-nullable. E.g. if
> Gender is an enum, just add a new UNSPECIFIED constant. This is much better
> for all clients. E. g. you may safely do person.gender().toString() to
> display the gender in the UI without ugly things like
> person.gender().map(Gender::toString).orElse(Messages.UNSPECIFIED_GENDER).
>
> With best regards,
> Tagir Valeev.
>
>
> But I would still use this pattern sparingly; the majority of uses I see
> > of Optional in domain models are gratuitous.
> >
> > On 12/14/2019 5:31 PM, Arash Shahkar wrote:
> > > In my example, gender can be left undefined, but not name. Dropping
> > > requireNonNull(gender) from the compact constructor works, but leaves
> > > the accessor for gender returning null (possibly).
> > >
> > > What I’d like to achieve is to have the accessor for gender return
> > > Optional<Gender>, to clearly communicate that it might be null. I have
> > > found this pretty common, and using this pattern has helped a lot.
> > >
> > > I was wondering if my “workaround” to be able to use records and drop
> > > Lombok for these cases is a big no-no.
> > > On Dec 14, 2019, 5:16 PM -0500, Brian Goetz <brian.goetz at oracle.com>,
> > > wrote:
> > >> No, that would not be a good idea. If you find yourself using Optional
> > >> for a method parameter, or a field type, or a record component type,
> > >> you've almost certainly gone Optional-crazy. It's a common, but
> > >> curable, disease.
> > >>
> > >> What you want to do is this:
> > >>
> > >> record Foo(String name, Gender gender) {
> > >> Foo {
> > >> requireNonNull(name);
> > >> requireNonNull(gender);
> > >> }
> > >> }
> > >>
> > >> The `Foo { ... }` is a _compact constructor_, which is a constructor
> > >> whose signature is the same as that of the record, and which
> > >> automatically handles the `this.x = x` boilerplate for you. This is
> > >> where you put argument validation / normalization.
> > >>
> > >>
> > >> On 12/14/2019 5:01 PM, Arash Shahkar wrote:
> > >>> Hi,
> > >>>
> > >>> I’ve been using Lombok for quite a while now to automatically
> > >>> generate boilerplate code for "data carrier" classes. However, a
> > >>> pattern I really like and have found to work very well is to verify
> > >>> non-nullity of all the fields that must not be null, and return an
> > >>> Optional<Field> from the accessor of a field if it’s allowed to have
> > >>> a null value. Consider:
> > >>>
> > >>> @lombok.Value
> > >>> class Person {
> > >>>
> > >>> String name;
> > >>> Gender gender;
> > >>>
> > >>> Person(String name, Gender gender) { … } // customize the
> > >>> constructor, verifying that name is defined and valid
> > >>>
> > >>> Optional<Gender> getGender() { // customize the accessor for
> > >>> gender, to make sure calling code deals with the possibility of
> > >>> gender being null
> > >>> return ofNullable(gender);
> > >>> }
> > >>> }
> > >>>
> > >>> Doing a similar thing with records is not possible, due to the fact
> > >>> that the accessor for gender has to exactly return a Gender. With
> > >>> the current specification for records, the only option is to do:
> > >>>
> > >>> record Person(String name, Optional<Gender> gender) {
> > >>> …
> > >>> }
> > >>>
> > >>> Is this considered good practice? Do you suggest any alternatives?
> > >>
> >
> >
More information about the amber-dev
mailing list