Skip to content

[REQ] [Java/Spring] Adds Accessors to Models #20453

Open
@slobodator

Description

@slobodator

Is your feature request related to a problem? Please describe.

Models (requests and responses) can have fields assigned to nulls. Handling them may cause famous Null Pointer Exception.

Describe the solution you'd like

I am about to add a new configuration boolean parameter modelAccessors.

Value Beaheviour
false (default) do nothing, keep current behaviour
true adds accessors according to the field settings
Fields Settings (private) Field (public) Getter (public) Accessor Comment
required:true Foo foo; Foo getFoo() Foo foo()
required:false, nullable:false Foo @Nullable foo; Foo @Nullable getFoo() Optional<Foo> foo() Getter and accessor are different only at this case
required:false, nullable:true JsonNullable<Foo> foo; JsonNullable<Foo> getFoo() JsonNullable<Foo> foo()

Once using that accessor instead of the getter will force the developer to property handle Optional types if needed.

Describe alternatives you've considered

There is an awesome @MelleD useOptional:true with a minor confusing issue and its further discussion. It you're not confused with Optional getters it definitely fits you.

Additional context

Importunely, there is no single approach of using Optional. First, I would suggest getting familiar with various camps https://nipafx.dev/inside-java-newscast-19/

I seem to be from the #2½ one with some extra thoughts. Let me explain my way of thinking.

Statement 1. I guess that fields within a class should be of raw types not Optional. Even I haven't encountered any issues with serialization yet, it is not recommended by Java fathers and static analyzers.

Statement 2. Thus, setters and getters should be of the raw types too. Just to follow the rules of Java Beans specification and not to confuse any framework. There should no extra logic within them, they should match their fields and types etc. Even Jackson Object could handle Optional and MapStruct is forced to do the same I don't think it is fine and correct.

Statement 3. I believe that using setters and getters at the business code is the second billion dollars mistake after NPE. The reasons for that are:

  • setters allow building an object in inconsistent state (a constructor should be used instead) and break the object encapsulation and its business constraints
  • getters make the object "naked" indirectly violating its encapsulation
    Thus, getters and setters (and empty constructors) should be provided for frameworks and not consumed by developers.

Unfortunately, there is no way to restrict that, and the worldwide habit is to use them making an object a structure managed in the procedural programming style

Statement 4. Despite the statement above, requests and responses are DTOs not encapsulated objects. Thus, it is ok and necessary to have direct access to their internals. As @Nullable annotation at the getter is just a warning and changing the getter signature is not allowed according to the Java Bean specification we need the 3rd way -- by adding the accessor. It will be of either a raw type or Optional one based on if the field is required or not as written at the table above.

If the field is non-required its implementation will be

public Optional<Foo> foo() {
   return Optional.ofNullable(this.foo);
}

To avoid any theoretical discussion, let's focus on its practical usage.
Once one is tired with handling NPEs in Java6 way

if (request.getFoo() != null) {
   // do something

... there will be 2 options for them

  • to use useOptional:true. It will lead to changing getters to `Optinal getFoo(), the project will be stopped being complied and this is actually fine as it forces the developer to review all usages of getters.
  • to use this feature of accessors. Still, if their colleagues are against the idea and keep using getters there will be no benefit.

The key point of the accessors is:

  • there are situations where one needs just to bypass the value. Assume, I am storing a JPA entity. JPA doesn't support Optional for fields out of the box AFAIK. Then I'm using the getter
repo.save(
  new Entity(
    request.getFoo() // or request.foo().orElse(null)
  )
);
  • there is some business logic based on the value. That the accessor should be used
if (request.foo().length() < 2) { // is not compiling, telling me that `foo` is optional, forcing to handle it properly
  ...
}

@MelleD There main question is to you as you're from the tech committee. Will you accept this PR? I know it contradicts your vision of Optional but it is a non-breaking change. Does it make sense to have an alternative implementation to Optional? If you strictly against it, I won't even start.

@Kavan72, @Moribund7, @mvdwalle, @jwilmoth-ehs, @jpfinne, @Chrimle, @robinjhector Let me tag you and ask for your opinion, please. Does the idea of accessors make any sense to you? You may just downvote or upvote this message, but I would appreciate your comments as well.

Well, if using Optional is such a holy-war topic it always is an option to customize the template, right?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions