-
-
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
[FEATURE] implements in Builder/SuperBuilder #2224
Comments
What's the use cases here? The interface to be implemented by the builder can only contain the |
I have an object storage and this builder is used in generics. so my framework only needs one method - build();
where Mutable<> describes toBuilder method and Builder only build() method. Storage manages saving and loading from/to Json and provide interface of changing beans through new builder updating json. I want to call build() inside service to make necessary decoration and checks so there is no object creation will occur in user's code. so we can write code with autocompletion like
also really can implement not only build in builder because builder setter can implement Interface methods; like in
another usecase is using builder as a default object provider. like java.util.func.Supplier to create new instances |
I think this is a good example of the customization possibilities: It's not common enough to become an annotation parameter, and it is really simple to do just by adding the builder header and an What I don't understand is why it should not be possible with Disclaimer: I'm not a Lombok team member, just a contributer. PS: If you only need the |
i already made this by using methods reference, but i got back to boilerplate code because i need to write this monster in each bean class.
so do i with builder implements. also Builder does not have common on* args. so we can't add Jackson annotation to builder without creating class, it's another problem, but it's very common in practice. Though we can create some global annotation like |
I think @maroziza and me disagree on whether there is significant value. I regard this use case as rather uncommon, and a case which can be easily mitigated by customizing the builder classes. I agree that this leads to a little boilerplate code in each class, but Lombok cannot support every use case. |
Sorry for spamming in this non-related thread, but is there some estimations about releasing out SuperBuilder from the experimental package? |
@savinovalex: Please use the search function, it has been discussed recently. |
@janrieke As a very happy Lombok user, I don't want to rock the boat unnecessarily, but I have to agree with @maroziza in this case. My use case is converting a project built with compiled protobufs, which have extensive builder typing and and builder materialization options. One of the methods I was depending on for a class mapper that doesn't use extensive reflection was Without a marker interface on the builder, the client code explodes with reflective checks on the various objects and creates a really painful developer experience. The solution I came here looking for was that they generated types implement (at least) a marker interface. But I think this feature request does a much better job for those that need this functionality, without interrupting the experience of the de-lomboked code for those that do not. |
I do believe you that there is a usecase. I just don't think that it is common enough to justify the effort the Lombok team has to make to get this working. Especially as there is a workaround (inserting the builder header) which is rather easy to do. I'm not a Lombok team member, but I contributed |
Maybe you are inferring "diminishing returns", which is arguable. But I think you are completely overestimating the effort required for this simple change versus the power it affords the community through its availability. And I may fully misunderstand how difficult this is since I've never looked at the Lombok source. Presumably it's using ASM for direct bytecode generation (EDIT: Eclipse JDT), but even that is not rocket science. (I caveat this with the fact I do not have a PR, but it doesn't diminish this request or imply that it ought to be closed.)
I honestly did not see this reference or understand what that means after I look through your comments. What is a "builder header"? Neither the In any event, I only noticed your comment from another laptop that doesn't get Github notifications because I came back to include that the |
As with |
@briantopping The problem with an overture to the 'power it affords the community' is that lombok has something around a million users (due to maven central, hard to have exact numbers), and it is obviously not feasible to reach them all for some sort of vote to get an accurate read on how useful this is. As a consequence we are primarily motivated with arguments along the lines of 'this common idiom (reference to 'proof' for example with a github search) would be easier if lombok feature X is changed like so', or 'this commonly used library (easy enough to show a library is commonly used; show your work here) requires that things look like X, so why not add a lombok feature?'. We are not motivated by the say-so of an individual, or even the say-so of a 100 upvotes. I wish I could introduce a system whereby we can submit a feature proposal to the lombok audience and see what people think, but every time I bring up popup boxes in IDEs, people immediately start shipping for pitchforks. It's just not an option. Thus, we have only one way to do this: Taking comments and our admittedly limited and no doubt biased view of what the greater java community wants, mixed in with what motivates us, we make decisions, and try our best. This one isn't making the cut. Yet. The best way to change our minds is to make specific references to libraries or idioms that would be better; this thread already has a few of these. I'm particularly interested in the protobuf angle. Some more commentary on how this interface stuff would help there would be useful. Protobuf is commonly used; that needs no further debate :) – but I haven't personally used it so I'm having a hard time following how it helps. Can you perhaps link to some github gists to show what you're trying to do? NB: We work on a 'default answer is no' model; the problem with saying yes to anything that seems easy enough to write, is that it leads to a builder annotation with 75 parameters, and whilst each individual parameter is easy enough to explain and test, having a feature page to explain 75 parameters is not good for lombok's learning curve, and trying to test the combinatorial explosion of 75 parameters takes a few thousand years, so it also hurts the stability of lombok: Hence, the burden of proof for a feature request is high, and will remain so. |
@rzwitserloot Thanks for the details here. As I explained some of this to the client this morning in a meeting prior to reading this just now, I said a lot of the same things. There are far too many critical uses of Lombok that would be endangered over time by weak curation of features. I don't think there's anything wrong with the path we're on, and it's up to external entities with the domain expertise to prove out the use case. Nobody reads minds! In general, the use case here is the polymorphic dynamic access to classes decorated with Start by considering an object-relational database mapper (such as a JPA implementation) that leveraged Could Even if we had such a In the client use case, they are using the R2DBC database connector and manually mapping rows to object fields with static code. This is static code so it's relatively fast, but very fragile as the schema of the database or objects change. I built them a primordial mapper in 50 lines of code that gets around this using the facilities provided by the Protobuf compiler, which also implements it's own very robust version of Builder objects. A major difference is the generated objects (both the DTO and it's Builder) is they conform to interfaces that exist in the Java Protobuf library (see above link). There's no reflection required to access these methods, only a cast in rare situations. If I were reading this, I'd still be asking myself "okay, where's the ultra compelling use case here?", and that's fair. OR mappers aren't standard engineering fare, we mostly take them off-the-shelf and R2DBC just isn't quite there yet. Should Lombok be concerned? My rhetorical answer would be "well, what are the use cases for Protobuf?" Of course there are innumerable use cases, of them is gRPC, which most will agree is a very elegant insofar its leverage of Protobuf. Will someone write a RPC platform using Lombok? Probably not if they don't have polymorphic abstractions to the objects being sent over the wire. Does it matter? If you ask me, I think Lombok is great, so of course I want to see compliments to it! There's usually some "tipping point" where paradigms are powerful enough for someone to have an epiphany and write something that catches fire. The client POC I referenced was already written with Protobuf and as I thought about it, a mapper was an obvious weave between R2DBC and the classes generated by the Java Protobuf compiler. The client is using Lombok and not Protobuf though, my landing on this issue was a result of trying to accomplish that port. And what I had to tell them this morning was it isn't possible without so much reflection it would be a pretty unusable solution. What can I do to help here? |
That does sound cool, but I see some issues with it; lombok currently ships essentially without any runtime dependencies. That would change if we introduce, either by default for all public interface DynamicBuilder<T> {
DynamicBuilder<T> setProperty(String pName, Object v);
T build();
// possibly: List<String> getPropertyNames();
// possibly: Map<String, Class<?>> getPropertyTypes();
// possibly: List<String> getMandatoryPropertyNames();
// this list of fun methods we could have is probably rather long :P
} ... and, of course, ship that interface. And therein lies the rub. Currently lombok doesn't ship any runtime deps; we'd have to create it just for this. I'm game, but it raises the bar for using it. In practice, IF people are already doing something like this, presumably they are already relying on an existing mapper system, such as protobuf, and they have an interface there that they'd want us to implement. Except each such interface is no doubt a little different. There's a way out: If protobuf has such a 'dynamicbuilder'-esque interface with The slam dunk use cases for lombok are where we encode an existing and already widely applied boilerplate pattern as a lombok feature (example: builders, We're trying this experiment right now with This would all be a lot simpler if 'dynamic builder' as a concept was already employed, with existing interfaces (even if not in java.*) we can use. As a side-benefit, the learning curve and support argument for So, I guess... the next step'd be to, uh, launch a new (or preferably, find an existing) library that is somewhat widely used with a DynamicBuilder-esque interface :) |
Great stuff, thanks for the consideration! I did forget about the lack of runtime dependencies. This is a huge oversight on my part and I think it's a "core elegance" of Lombok – it kind of "just disappears"... Which is ironically a great plug for It's also kind of a "gateway drug" to the What if Lombok
I think there is a fourth group, which is the one that Delomboks the code and then says "wow, this code is complex, muh rights are being violated and I'm going to complain anonymously on Reddit!" Though I think this is a fleetingly small group. Most people who stumble upon Delombok and don't understand what just happened quickly select "Undo" or In the local use case I have here of generating a "best practice POC" for the client, I'm not sure how I feel about generating a Maven / Gradle subproject just for the Lombok extension if that were my only way out. Imagining that the extension would have code that looked like this, I think the client would be like " Lastly, to your point about |
I may be completely mistaken concerning your goals, but it looks like you wanted the most efficient conversion from database rows to entities and the most efficient way includes no The most efficient way includes code generation. AFAIK what Hibernate does is to generate an SQL returning all columns and to generate a method populating your fields like
Hibernate uses reflection in order to get the field list (both for the SQL and for populating the entity) and it generates the method using bytecode generation (gclib, bytebuddy or whatever). This is just the initialization; everything else uses the generated code which is as fast as manually written one. Instead of reflection + bytecode generation, you could use annotation processing (assuming all entities are available at compile time). That all said, I'm not opposed to this feature. I may also be completely misunderstanding your needs. |
Thanks @Maaartinus, I should have been clearer about when I was optimizing for efficiency and to what degree. There are cache-free implementations, implementations that cache invocation proxies, and bytecode generation. A In the case of the mapper I was working on, I was focused on as few lines of code as possible so the core pattern would stand out. The Protobuf compiler I was using didn't actually use dynamic access for In the end, these were just use case examples for how Lombok could benefit from tunable code generation. Apologies again for the misunderstanding. |
I also see the benefits of having such an dynamic builder set method. It would be really great if there was something like a common interface for this. Imagine lombok would generate builders that implement it; then frameworks like Jackson could simply make an I know the Spring Data folks like lombok, they specifically recommend it in their docs: "Use Lombok to avoid boilerplate code". If we could get them, the Jackson people and maybe someone from JPA/Hibernate onboard, this could be the start of something. |
Apologies for sounding negative... I'd really love to see Lombok nicely integrating with other tools and vice versa. I'm just unsure whether
Unlike when processing SQL result set, there's no fixed field order, so a regular set method can't be used (assuming you get an incoming JSON object and process its fields as they come rather than reassembling somehow, which IMHO is inefficient). So with Lombok, you'd probably get the maximum speed possible. OTOH the only gain is saving the reflective access. I tried to find something about the reflection overhead, but the SO answers are not only obsolete, but also a crap (no warmup and OSR in the accepted answer). When performance is the goal, then we should have a JMH benchmark. |
DZone has an article on that topic, using JMH and Java 8. It looks quite solid to me. Besides the performance argument, such an interface would make writing frameworks that instantiate POJOs much easier: Just require the POJOs to have a builder conforming to that standard interface, and you do not have to deal with reflection, code generation or similar stuff at all. PS: @Maaartinus I do not understand your point on "fixed field order". How is the order relevant when only using statements like |
@janrieke
I guess, it's fine. What I'm missing there is bytecode generation, which is sort of standard and surely has much lower bootstrap cost than the Another thing: The benmark compares the performance of Actually, Given the above idea like
I wonder, whether |
It would be great to add implements argument to [Super]Builder annotation to implement general builder interface.
With this feature
without this feature we can't implement SuperBuilder at all, and need to use boilerplate for regular Builder:
as an option default interface LombokBuilder can be provided
The text was updated successfully, but these errors were encountered: