-
-
Notifications
You must be signed in to change notification settings - Fork 6.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Using Optional.ofNullable() at the fluent setters to prevent NPE #20406
base: master
Are you sure you want to change the base?
Using Optional.ofNullable() at the fluent setters to prevent NPE #20406
Conversation
Here are some points. It‘s not really a bug it‘s intended the behavior. I would never use a null value to set an optional to empty. I would prefer a that you can configure what you would like to use: Optional#ofNullable or Optional#of. Another point is maybe you should check this PR that you have maybe use some Nullable annotation as well. @wing328 can you remove the bug label please |
Could you elaborate on that, please? I still can't get the idea
You mean you never write new Payload()
.name(null); ...? Indeed, no one does that. Still, let me explain the issue, please. Assume, I am writing an endpoint that accepts a body with non-required parameter (I changed public Optional<String> getName() {
return name;
} ... gives me a clear indication to handle it properly. Now let's look at this from the client perspective. var payload = new Payload()
.name(inputStr);
restClient.post(url, payload); If the "F*ck!" say @Kavan72 and @Moribund7 and @mvdwalle and @jwilmoth-ehs. var payload = new Payload();
if (inputStr != null) {
payload.name(inputStr);
}
restClient.post(url, payload); Someone may say that they prefer a builder for the fluent object building but var payload = Payload.builder()
.name(inputStr)
.build(); ... won't help as the builder Or do you want us to use a regular setter i.e. var payload = new Payload();
payload.setName(Optional.of(inputStr)); ...? "F*cking f*ck again!" says the team as it still throws the very same NPE!
So, the only correct way is var payload = new Payload();
payload.setName(Optional.ofNullable(inputStr)); ... right? So, I guess that the fluent builder method in its current form public Payload name(String name) {
this.name = Optional.of(name);
return this;
} .. is totally inconvenient/useless for In what scenarios do you use it? Just in case. At the required arguments constructor you wrote P.S. @wing328 Please, don't remove the bug label :-D I hope I could convince @MelleD that |
To be clear I didn't write that. I was updating that (written by someone) to fix something if I remember correctly |
The fluent API in combination with the Optional.of() is very messy. I never understood why Oracle added the of() method anyway as it still forces you to do null checks everywhere. The case here is that mapping from for example a domain object, a result from the database or other sources where certain fields might be null gives a lot of headaches. @MelleD can you explain why it is intended? I do understand not calling the setter IF you already know it is null, but with the fluent API and mapping from sources that I mentioned before you cannot always be sure without added all the null checks. I really hope you reconsider this as (in my opinion) a simple change from Optional.of() to Optional.ofNullable() has enormous benefits without any serious impact. @MelleD You also mention that you do not consider this a bug. Is it not a bug if users of the library need to jump through hoops just to make it work? |
I've written the allArgConstructor and the builder. The handling of optional for DTOs is very messy. Second, the implementation is very bad. It would be much cleaner to just change the getters:
Instead optional fields are used.
Third the fluent setters can throw NullPointerException. The main design goals of Optional are:
If we want 3 states, we should use @slobodator your fix is the safest and easiest In the mid term we should review the implementation of Optional for the domain. |
No member variable are fine and good. There is no reason to change it. Why it should be cleaner? Thats just personal taste. Also I like the pattern that you have two constructors.
Yes documentation looks out to date. Also there are some request to separate it with 2 config parameter e.g.
Look into the PR it was discussed what we should use. So it was a decision what we should use. If it was an intentional and clear decision, it can't be a bug. But does it matter whether it's a bug or not?
new Payload() A lot of people would like to do that ;)
There was a lot discussion. If my/our code gets a null in your place, we have a bug somewhere and would like to detect it early. It is also always tricky to share client and server. The change could be helpful for the client, but it could be dangerous for the server, as in our case.
Thats a good question why do you have a inputString which is not directly an Optional ;), but yes thats different between the Optionals camp. In camp Optional everywhere your inputString is directly an Optional.
That is what Iam missing |
I followed all links from this comment and found this discussion, but still can't the reason. @welshm suggested the same at the very beginning.
Sorry, still trying to convivence you. Let me try to use this way, please. I am writing a client call to some endpoint.
My bad, I didn't mention the spec meaning it is obvious. openapi: 3.0.0
components:
schemas:
Payload:
type: object
# name is not mentioned at required
properties:
name:
type: string The codegen settings are
That definitely makes sense for converting
I do like it too but let's assume So, I am writing either var payload = new Payload()
.name(inputStr); ... or var payload = Payload.builder()
.name(inputStr)
.build(); ... if I like the builder more. I am a client; I don't even look at the generated code.
What did I wrong at the code above? "How should I build that payload?" the team is asking.
Optional<String> inputString...
new Payload().name(inputString); That's awesome that you mentioned it.
But currently it is So, I see two options.
public Payload name(String name) {
this.name = Optional.of(name);
return this;
} ... is useless actually.
What is your concern? How does it affect the server? |
Yes you are totally right. Personally I would avoid
Perfect would be for Optional this
|
@MelleD After your message above I guess I got your way of thinking. You seem to follow the set of rules:
It looks like a Kotlin way with Java tools. With some compiler assistance, but the main rule 1 is on the developers. I am not going to discuss the pros and cons of this approach, I will say just two sentences
I finally got what you meant and read it till the very end. So, a lot of people staring with @robinjhector are trying to tell you that
For you it is obvious and fine that var payload = Payload.builder()
.name(inputStr)
.build(); ... throws a NPE if rule 1 and rule 3 are violated. It is a punishment that But... it is NOT obvious for others. (Just in case, I am not going to start discussion "constructors vs builders" even my personal opinion is here. Folks are using builders, it is a fact) They check the spec, see that the There is no documentation of this behavior. And the "funny" fact is that you don't use that builder method yourself preferring regular setters! You didn't add the builder method Even a guy from the Optional-everywhere camp with the current implementation should either
Optional<String> inputStr = ...;
var builder = Payload.builder();
inputStr.ifPresent(s -> builder.name(s));
var payload = builder.build(); ... as there is no way to write it in the single call/fluent style.
Is the only way to use a fork/customize the template or could we convince you somehow? |
Thanks @slobodator for your efforts I would do:
Clear for everyone, backward compatible... |
What's the point of
|
Just a clarification: I don't want 3 states optional So the setters should.not accept null values.
|
And again an endless discussion begins. If someone don't want to use an Optional, they don't have to. You can also have raw arguments and handle the For this reason, the rule is very simple and is default for spring projects. And again you can introduce Config a)
Config b)
Additional feature and independent is to allow in the builder pattern also Optional directly.
Strange to break something what was no there before! I have the feeling people don't want to use Optional and don't see that they can just turn it off and the code is the same like before my PR. There is no obligation to use optionals. And again we can discuss for pages, but why don't you want to make it configurable, then everyone can set it as they want and everyone is happy. |
This is definitely a good idea, and I encourage everyone to do the same! Or did I miss something, and you kindly point me to Spring documentation now? I guess, the root cause of our discussion is that the builder method is not annotated. You guess that
Folks used var payload = Payload.builder()
.name(inputStr)
.build(); ... before introducing @robinjhector told you that.
There will be extra efforts for
I was not going to do such much, just to fix 2 lines at this PR. Before starting it, I really need to get who will ever use that config B (current behavior) and for what purpose. public Payload name(String name) {
this.name = Optional.of(name);
return this;
} ... should be at the form /**
* @param name -- is mandatory!
* if you want to pass nullable parameter here -- check it against null first!
*
* @throws a NPE if name is null
*/
public Payload name(String name) {
if (name == null) {
throw new RuntimeException("name is null but it should be never null, see rule 1");
}
this.name = Optional.of(name);
return this;
} Who needs this? How this small change from How strict is rule 3 at your world?
And a kind reminder -- it is your rule, not Spring one or Java world one. May I tag the tech committee again? |
This is the key here. The fact that you're introducing your personal code preference, in a non-backwards compatbile runtime-breaking fashion. |
It was a new feature and you can turn it off. Everyone just ignores that. I don't understand!
What yes for every configuration is extra effort same for collection nullables. Create a config option here: Thats it
I will use it (e.g in test or or or) with the extra method to set Optional and no you have not to throw a extra exception.
Of course it is a recommendation and there are recommendations in the java world one example or Where is it described and what sources do you have? So far I only see personal opinions and you act as if you speak for all Java developers. And there are also other projects with the same discussion: What is the solution? Ahhh a configuration that everybody can decide what is right or wrong for the project And friendly note: You are discussing here as if you are all right and it is a rule how you want to use it. There are also many other opinions or do you think all happy users without problems are checking in here? https://nipafx.dev/inside-java-newscast-19/ For this reason it should be easily configurable. |
Some also use optionals for hidden if checks. Just because it works doesn't mean it's good.
Just like toogle and boolean method parameters, but there is a recommendation. However, not everyone does it. |
The Config B, is a prime example of when not to use public Payload name(String name) {
this.name = Optional.of(name);
return this;
} Because it does not allow public Payload name(String name) {
this.name = Objects.requireNonNull(name)
return this;
} Since For use-cases where |
It’s your personal opinion and your personal code preference |
I think everything has been said. I'm opting out. I have provided enough sources and evidence. Mind you, I'm the only one so far, but I'm accused of it being my personal opinion. First of all provide your own sources and evidence beforehand you blame others.You can also turn off the feature and use the code like before. @wing328 your decision. You have to go forward. I stick with it - there is no wrong or right here and there are 100 different approaches. |
It's really not. It's just incorrect use of |
I can only speak for myself, but code preferences - which often comes from experience - is often very difficult to convey, phrase and be coherent in a medium such as a Pull Request thread. I would suggest moving the discussion elsewhere - or await input from the tech committee? My view on Optional, to not bloat this thread any further. |
I could explain at least my motivation. It is a nice feature, and I would like to use it. I work for a company with a lot of teams and countless number of microservices. Not all of teams are even using OpenApi Generator, some of them still prefer Swagger. Even I have my own rules of code, I can not dictate them to other teams. So, I am reviewing generation trying to improve it to bring values for colleagues and community.
P.S. Thanks for |
Hi, tech committee member here. I propose the following course of action:
Whether That the introduction of Turning off Also, OpenAPI Generator (with library=spring and useOptional=true) is currently not designed with the fundamental idea that everything is non-null by default. And especially not with the idea that every public method must fail on any @MelleD, as another tech committee member, do you agree with my proposal above? (If not, I guess @wing328 will have to decide.) |
I have already written my preferred solution. I would introduce a new parameter/config (e.g. optionalAcceptNullable) and everyone can decide for themselves. |
Motivation:
When using
useOptional:true
it is worth to generate fluent setters that could acceptnull
values properly.Thus, they should be wrapped with
Optional.ofNullable()
notOptional.of()
, i.e. havingits fluent builder method should look like
instead of
Issues:
Modifications:
pojo.mustache
improvedtests adjusted
Read the contribution guidelines.
Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
Run the following to build the project and update samples:
File the PR against the correct branch:
master
(upcoming7.x.0
minor release - breaking changes with fallbacks),8.0.x
(breaking changes without fallbacks)If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.
@cachescrubber @welshm @MelleD @atextor @manedev79 @javisst @borsch @banlevente @Zomzog @martin-mfg Please, have a look