Allow a method to be invoked after @<X>ArgsConstructor
Many times, we want to avoid the tedium of writing constructors, but still do some initialization or computation on non-final / non-injected fields. Right now, the only option is to use Lazy getters, but that is cumbersome and not safe because the computation happens at a different lifetime. A PostConstructor would greatly help reduce the pain of boilerplate while still doing something useful (like validation, precomputation, caching), and also catching initialization errors early instead of using a Lazy getter.
@PostConstructor method annotation@<X>ArgsConstructor
public class SomeClass {
private final int var1;
private final int var2;
private int computedVar3;
@PostConstructor
private void someInitMethod() {
this.computedVar3 = var1 + var2;
}
}
postConstructor annotation attribute(like onConstructor attribute on XArgsConstructor annotations)
@<X>ArgsConstructor(postConstructor="someInitMethod")
public class SomeClass {
private final int var1;
private final int var2;
private int computedVar3;
private void someInitMethod() {
this.computedVar3 = var1 + var2;
}
}
public class SomeClass {
private final int var1;
private final int var2;
private int computedVar3;
public SomeClass(int var1, int var2) {
this.var1 = var1;
this.var2 = var2;
this.someLazyInitMethod();
}
private void someInitMethod() { ... }
}
The sum of all listed exceptions (runtime or not, lombok doesn't know, and it doesn't matter) on all postconstructor methods will be added to each generated constructor. Seems the simple and obvious solution.
The biggest issue with this proposal is what to do with explicitly written constructors. The options are:
@PostConstructor at all in this case. Usage is an error.@PostConstructor to specify what to do for explicit constructors (add or not). Failure to set this explicitly means you get a warning (or even error) if explicit constructors are present.lombok.config) to set what you want: Error, Warning, Inject, Ignore.NB: Trying to inject these calls into existing constructors is probably really difficult, because java. There can be returns, infinite loops, etc, which means 'just add em at the very end, just before the closing brace' isn't guaranteed to actually work, so.. where then? Hence any choice where injection into existing constructors is necessary means we need to tackle that problem first.
A separate issue (and I should really write a post about this for the main site) is that our market research (as in, when we have beers with lombok users at various conferences, and we listen to what they have to say.. we listen better when the beer is handed to us, by the way) shows that the vast majority of lombok users DO NOT use most of the features and aren't even aware of the new ones, therefore, either we find a way to 'educate' about the new features, or we really should focus on making the existing ones work better, and lombok improvements as a whole (such as making the eclipse support plugin based, and adding to this plugin refactor support to lombokize existing code).
Thus, even if this proposal ends up in a 'this sounds like a great addition' kinda place, I'm still tempted to veto it just because that's apparently not the direction we need to push lombok right now.
I guess, for now, "Do not allow use of @PostConstructor at all in this case. Usage is an error." is good enough as covers 90% of cases and is definitely not going in the way later.
As a next step, you might want to implement handleExplicitContructors=false and error out when true is given (you allow an explicit constructor, but force the user to tell you you should ignore it). This sounds strange, but is future-proof and handles a good portion of the remaining cases (or all of them when requiring the user to add an explicit call themselves counts).
For the far future, a combination of lombok.config and an option seems to be perfect. The option alone should be sufficient as the combination of this feature and an explicit constructor is hardly common enough to require a configuration.
_It is possible to call it @__In__Constructor? It's more precise and less colliding._
Thus, even if this proposal ends up in a 'this sounds like a great addition' kinda place, I'm still tempted to veto it just because that's apparently not the direction we need to push lombok right now.
The base variant should be really simple and covers many use cases, so I'd suggest to implement it. Improving it by considering explicit constructors is probably much harder and I can understand if you postpone it.
Thanks for considering this.
For handling explicitly written constructors, I think it depends on the approach:
@PostArgsConstructor on method, then this becomes ambiguous with explicit constructors, and hence I support the error behavior@<X>ArgsConstructor(postConstructor="...") then this is tied to the existing behavior, and can just ignore explicit constructors as it is nowI somehow like (2) because it seems to integrate well (in my tiny knowledge) with current Lombok, and it gives more control to the app developer. Because there are some ridiculous corner cases, like an explicit constructor that is written to invoke the postConstructor method at a different line number (not first or last, but somewhere in middle). In general design terms that is _really really ugly and bad_ on the consumer side and I would never recommend this in my own projects, but then I wouldn't want Lombok to enforce that either 😄 Would be nice to keep the view that explicit constructors just know what they are doing (good or bad) and are out of Lombok purview and into the general java compiler zone.
I agree with how practical such a feature would be. As a personal use-case example, I've got a @AllArgsConstructor(staticname="hydrate") on my JSR-303 annotated entities, it would be really – really ! – helpful if I could do a postConstructor="validate", with validate() performing the bean validation.
Auto-inject into explicit constructors by default. Allow an annotation on explicit constructors to disable auto-injection.
@yyy I would rather not touch any existing constructor logic. If the class had no explicit constructor, and relied on super() being called, then we'll have to artifically create an empty constructor for the class, and infringing into territory of NoArgsConstructor. Similarly, Other
I would vote strongly for adding postConstructor="" attribute to the default lombok @xxArgsConstructor, instead of using a @PostConstructor separate annotation, because the latter just opens up more questions than it solves. I proposed this feature only for autogenerated constructors. For explicit constructors with my own code, I already have the power to decide whether or not to call a method, and I don't need lombok for that (in the good sense).
I would vote strongly for adding postConstructor="" attribute to the default lombok @xxArgsConstructor, instead of using a @PostConstructor separate annotation
The attribute is "stringly-typed", which is IMHO too bad.
What about @PostGeneratedConstructor?
Obviously, it does nothing to explicit constructors. The name is longer, but at the point when the handling of explicit constructors gets implemented, this annotation may be deprecated in favor of the new shiny all-mighty @PostConstructor.
@Maaartinus a) it would follow existing conventions/style like @EqualsAndHashCode(of=...), and b) its not stringly-typed as in runtime evaluated, but its caught by the compiler. So its not too bad, there are both pros/cons as with any other choice.
But frankly I have no specific preference, because what matters now is @rzwitserloot helping to make a choice. Having this in one form or another would already be a big plus 😄
I must admit I got into a habit of creating a private void validate() method whenever I need to validate the arguments to a constructor. It just seems cleaner than cluttering the constructor with the validation.
So to me, the ability to use an annotation like postConstructor="validate" would be a really valuable addition!
Being able to specify postConstruct method would be really helpful and should be very possible +1, just stomped on this issue.
Thanks for this feature request. Is there any update on this as I have a whole lot of refactoring work coming up.
Also waiting for this feature - Its would be very helpful for example to trigger validation or generate a hash after construction
I actually like this feature and would like to implement it if @rzwitserloot or @rspilker would accept a PR (including test + documentations). I would start with the easy solution suggested by @Maaartinus.
About explicit written constructors: I think we can wrap the whole method body (excluding super()) in a try ... finally block and call the @PostConstructor methods in the finally part.
I figured out two ways to inject the calls into explicit written constructors:
Before:
Class() {
if (a > b) return;
if (b > a) throw new Exception();
b = 10;
}
Wrap every return statement into a block and just add the calls there:
Class() {
if (a > b) {
postConstructor();
return;
}
if (b > a) throw new Exception();
b = 10;
postConstructor();
}
Wrap the code into a try ... catch .. finally block and call postConstructor() in the finally block:
Class() {
boolean $callPostConstructor = true;
try {
if (a > b) return;
if (b > a) throw new Exception();
b = 10;
} catch (Throwable t) {
$callPostConstructor = false;
throw t;
} finally {
if ($callPostConstructor) {
postConstructor();
}
}
}
Both should work in all cases, I prefer the first one because it is shorter as long as there are only a few different exit pathes and it looks like something that could also be written by hand.
Another way to enable modifications of explicit written constructors is a new annotation @PostConstructor.Include. That one gets added to the constructor. This would increase flexibility (for the 1% that might need that) and improves readability as it gets obvious that there is some post construction logic somewhere.
Most helpful comment
Being able to specify postConstruct method would be really helpful and should be very possible +1, just stomped on this issue.