-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
[BUG] Builder Constructors are protected when should be public in 1.18.18 #2768
Comments
Just FYI, I need to use the constructor method to avoid massive code changes to legacy code which already use this. |
I think that this is intentional, at least the documentation states that it generates a package private no-args empty constructor. If you need an additional constructor you can simply add one. @Builder
public class Data {
@NoArgsConstructor
public static class DataBuilder {
}
} |
Yup, it was intentional (and the access level thing is referring to the access level of the static But, I do get your pain. I'd like to cater to it if I can, but adding a permanent ugly, hard to fathom wart in the form of an option in the However, @Rawi01 's fix should work for you, no? If that suffices, that'll be the way to go, I think. |
I understand the desire to have a clean API. However, @Rawi01's suggested fix doesn't work because in that case lombok tries to create two empty constructors with only the visibility being different which obviously doesn't compile, so to make it work, it would have to be written like so:
|
The duplicate constructor problem could be fixed, I guess. Factory methods like Furthermore, it is a well-established idiom in the builder pattern. Adding a public constructor could confuse users, because they don't know if there is a semantic difference between the constructor and the factory method. |
Yes, that's what I said - in theory it can have benefits, however, in the current implementation it does not - or am I missing something? To my knowledge the builder() method just does this when compiled to Vanilla and nothing else: |
OK, so new suggestion: what if we make it configurable to have a public builder constructor and no factory method OR have a factory method and a hidden builder constructor? That way you have a single option for each class at any given time to create the builder. |
As I explained, the factory method does have real advantages in practice right now. That's not theoretical, I used that approach of adding additional builder methods 2 times in the last 3 weeks. As @rzwitserloot said, making this configurable is not a good solution either, given the rare use cases where this is needed. So IMO the way to go is fixing the problem with @NoArgsConstructor on the builder. |
I can't comment on how "rare" our use case is I have no data to confirm or deny. |
Let's think this through. Pick a stylistic choice; pick any. You'll always be able to find someone who will say "It is legitimate" and "I prefer it this way", for both available choices, even if the choice is incredibly exotic. That isn't trying to indicate that your preference here is exotic or nuts. Not at all. I'm merely pointing at that 'random joe said so' is a meaningless indicator. Thus, your request boils down to three options:
I hope it is obvious that the second option, even though it's clearly what you want here, just isn't on the table, and cannot be. It's not a matter of us trying to hamfistedly force our style preferences on you. Or at least, that's not the intent. We try to go by the third option, but right now I'm quite convinced that the right move is to close this issue:
Now, I'm not sure that 'static methods' are significantly more common than 'public constructors in the builder class', but from my experience I think that's the case, and that's primarily why I don't want to add a feature for this, especially considering that there is a workaround. Having said all that, I'd still like to fix the Given the traffic on this issue so far, here's my proposal: Without further significant feedback (e.g. in the vein of: Look at all these very commonly used APIs that have public builder constructors! - that might change things), this issue is to be closed, and a new issue created to deal with the |
Note that the private constructor causes this Jackson issue. Basically, you can tell Jackson to use a builder (maybe using For the record, I could make it work using SebestyenBartha’s method, but only with a hand-written public constructor for the builder, not with (An unrelated issue is that if you set |
I'd say: The issue is with Jackson for not supporting a well-established builder idiom. Don't get me wrong: I don't want to blame Jackson. They need to make choices similar to the one the lombok maintainers had to make, and they chose differently (at least until now). But only because one particular library (although it is quite popular) did so, this does not change the point @rzwitserloot was describing: The maintenance burden for such a feature is probably much higher for a code-generator like lombok than for a "regular" library like Jackson. So I'd try convincing the Jackson folks to add support for specifying a static builder method name. That has advantages even without lombok (e.g., you can use a different builder for Jackson than for regular programmatic use). |
I see your point. But I do think that just turning a constructor from On the other hand, there is the issue that in some situations library consumers can upgrade Lombok much easier than Jackson. Jackson is a common shared library, all sorts of other libraries I use depend on specific versions of it. Even if I send Jackson a PR and they release a new version tomorrow I may not be able to actually use it for years. (Most of my job is making Jira add-ons, and in that Jackson is provided by Jira, and the next time it’ll upgrade to a version released today will probably be years from now.) In contrast, Lombok is pretty much compile-time only, I can just upgrade it whenever and get the benefit right away. (Also, note that the principle says “preferably only one”. I agree that APIs should be as simple and clear as possible, but right now the API is so simple I just can’t use it, and the distance to an API I could use is exactly one replaced word. Besides, it’s not like constructors are a weird and obscure Java feature.) |
I think it would be very valuable to be able to have the lombok generate the builder constructor public. As it stands now, Mapstruct and Lombok will not play nice together since they assume differently about how builders work. Given the variety of how various projects assumes object creation is handled, I think it would be beneficial if lombok could provide the task of easily providing these strategies. In my current project we've got objects with inheritance and need to use SuperBuilder, having lombok set that objects constructor to public, so that also Mapstruct could reach it would make our need to provide hand written boilerplate code much less. |
@arnljot Then tell mapstruct. Their library sucks because it can't deal with a very common pattern. And I'm not just saying common because 'lombok works like this' - it's in many projects. |
I agree that both Jackson and Mapstruct suck because they can’t deal with a common pattern. But also Lombok sucks because it can’t provide a common pattern, used by many projects. Can’t we make Lombok not suck while we’re working on making other libraries suck less? |
No, because catering to multiple common strategies that broadly to the same thing is ordinarily irrelevant unless trying to lombokize existing things without wanting to break backwards compatibility, or work around limitations in other projects that require a specific pattern to function properly. That's all too exotic, because the problem is: How do we 'document' and manage not to asplode settings into a combinatory disaster where most options are complete gobbledygook to most users? The answer is to aggressively prune that tree and pick preferred patterns. You've misjudged lombok. Its purpose is not to replace all imaginable boilerplate. Merely some boilerplate. On the flipside, surely mapstruct's purpose is to deal with all (common) patterns of structures that mapstruct can apply to. Hence, it's on them, not on us. |
Describe the bug
The constructor method to obtain a builder does not have public accessibility even though this is default. It does not respect the accessibility parameter either. The Class.builder() method works as expected.
To Reproduce
Expected behavior
new Data.DataBuilder() should be public. Explicitly specifying the access as public doesn't have an effect.
Version info (please complete the following information):
The text was updated successfully, but these errors were encountered: