Description
Is your feature request related to a problem? Please describe.
Models (requests and responses) can have fields assigned to null
s. 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?