Overload lombok setter - java

We can use lombok to generate setter like this:
#Data //or #Setter
public class Test {
int a;
}
Say for instance I also want an overloaded setter that would take a String:
public void setA(String aStr){
//parseInt and set 'a'
}
But when I add this overloaded method, lombok thinks that I have manually added a setter and so it chooses not to add one itself.
Apparently it looks only at the method name and not the parameters.
Is there a way I can force it to add the normal (that takes an int as parameter) setter?
Or the only way is to add that normal setter myself (using IDE setter generator of course)? I have a lot of fields and a lot of classes.

Adding the #Tolerate annotation on my overloaded method solved the issue.
Documentation:
Put on any method or constructor to make lombok pretend it doesn't
exist, i.e., to generate a method which would otherwise be skipped due
to possible conflicts.
It has been experimental though, since 2014.

The documentation states that "No method is generated if any method already exists with the same name (case insensitive) and same parameter count.".
This is the case that you've described. Instead, you should define an additional custom setter method with a new name like
setAFromString(String aStr)

Related

How to run methods before and after Lombok #Setter

I want some of my methods to run before and after the Lombok #Setter.
For example:
#Setter(after="save")
private String name;
This should run method called "save" after the original setter has assigned a value to the field.
So after compilation it should look like this:
public void setName(String name){
this.name = name;
this.save();
}
Maybe there is something I could do with "onMethod" parameter? Sorry, I'm not professional in annotations.
There is currently indeed no way to do this; onMethod isn't going to help either (SOURCE/DISCLAIMER: I'm a core lombok dev).
We do have some plans to add such a thing, but before you go ahead and write a PullRequest to add such a feature, discuss it first. There are various ways to go here. The primary issue is syntax.
You can't put java code in an annotation (you can put it in a string but we veto such a move; your IDE is not going to help you, it's got the wrong syntax highlighting, and so on). You can't, unfortunately, put a method reference in an annotation either. You could put a string in there that mentions a method but we don't like that either (again, wrong colouring, no IDE autocomplete support).
That leaves magic naming (you make a method called afterName), but at that point lombok saves you nearly nothing, so why not just write the setter. A second option is to have a single 'validate' method that is called after any setter and is e.g. marked by an annotation. That's more the direction we're leaning into.
There's more to consider: Sometimes you'd want to mutate the incoming value before assigning it, and the same mutation should be done in case you have a builder, or have a constructor that accepts this param, as well. Thus, we have:
Post-set actions after any setter and construction, such as save().
Pre-"setting", validate and/or mutate, after any setter and construction. For example, let's say you have a setAge() method and you want to throw an exception if someone attempts to pass a negative value. The set shouldn't even happen, so a post-set handler can't do the job (it'd be too late, the object is already in an invalid state and you'd want to avoid this). Complication: Given that we want to do this pre-setting the field, how does this method get the values, then?
Hopefully that illustrates why this feature isn't (yet) in lombok and why it's a matter of a simple PR to add it.

Mapstruct not selecting overloaded method for optional properties generated by Immutables

I'm using Immutables to generate some classes DTO classes, and Mapstruct to map JPA entities to DTOs. This works great, as long as the DTO has only required properties. If a property is not required (by making the accessor method return an Optional, as per the docs), the builder generated by Immutables will have a setter method with an Optional argument, which Mapstruct will fail on:
error: Can't map property "java.lang.Integer id" to "java.util.Optional<java.lang.Integer> id". Consider to declare/implement a mapping method: "java.util.Optional<java.lang.Integer> map(java.lang.Integer value)".
Makes sense, thankfully there's an option available to also generate methods that take nullable arguments instead. This option generates an additional method that takes a nullable argument. However, Mapstruct seems to fail regardless of the presence of this method.
As a workaround, I implemented this abomination (but at this point I'd rather implement the mapping methods myself):
#Mapper
public class OptionalMapper {
public <T> T unwrapOptional(final Optional<T> optional) {
return optional.orElse(null);
}
public <T> Optional<T> wrapIntoOptional(final T value) {
return Optional.ofNullable(value);
}
Is there any way to make Mapstruct look for overloaded methods (or see the "correct" one first)? Am I going about this the wrong way or simply missing something? Thanks!
Currently writing that custom OptionalMapper is the way to perform the unwrapping of the optionals.
I don't think that doing that is a bad thing. There is an open issue for supporting Optional

Is #NonNull annotation of Lombok allows the default constructor to give null values?

I have a class say C
class C {
#NonNull
private String str;
//getters
//setters
}
Now I did something like this :
C ob = new C();
System.out.println(ob.getStr());
To my surprise it printed null.
However, it gave Null pointer exception when I did:
ob.setStr(null)
Does #NonNull does not hold on default constructors? Please explain.
Does #NonNull does not hold on default constructors?
Indeed no, it doesn't, and I don't see how it could. When a default constructor is provided by the compiler, that happens during compilation, after annotation processing.
Moreover, Lombok's own docs have this to say of that annotation's use with fields:
Lombok has always treated any annotation named #NonNull on a field as
a signal to generate a null-check if lombok generates an entire method
or constructor for you, via for example #Data. Now, however, using
lombok's own #lombok.NonNull on a parameter results in the insertion
of just the null-check statement inside your own method or
constructor.
That is, the annotation has effect only on your own constructors and methods (i.e. those present in your source code) and those Lombok generates for you. A default constructor provided by the compiler is neither.
Please read the documentation...
Lombok has always treated any annotation named #NonNull on a field as
a signal to generate a null-check if lombok generates an entire method
or constructor for you, via for example #Data.
You are using the default constructor, so Lombok is not generating a constructor for you, therefore there is no null check.
If it did what you were expecting, every default constructed object of your class:
C ob = new C();
would immediately result in a null pointer exception. Not exactly very useful.

Custom `NonNull` annotation in Lombok

Here's the java-doc of NonNull annotation of Lombok:
If put on a parameter, lombok will insert a null-check at the start of
the method / constructor's body, throwing a {#code
NullPointerException} with the parameter's name as message. If put on
a field, any generated method assigning a value to this field will
also produce these nullchecks. Note that any annotation named {#code
NonNull} with any casing and any package will result in nullchecks
produced for generated methods (and the annotation will be copied to
the getter return type and any parameters of generated methods), but
only this annotation, if present on a parameter, will result
in a null check inserted into your otherwise handwritten method.
WARNING: If the java community ever does decide on supporting a single
{#code #NonNull} annotation (for example via JSR305), then this
annotation will be deleted from the lombok package.
If the need to update an import statement scares you, you should use
your own annotation named {#code #NonNull} instead of this one.
What is the simplest way to have my own annotation, let's say NonNullNonnull, and Lombok to inject null-check based on my annotation?
Update: my question is hot to have an annotation to use for method arguments.
First, you need to name it nonNull (casing is irrelevant). NotNull will not be recognized by Lombok. Additionally you need to set another Lombok annotation (e.g. #Data, #Setter, ...), so that your type gets processed by Lombok.
Summarizing your custom annotation isn't probably as valuable as the #lombok.NonNull-annotation itself. An example where you benefit from the #lombok.NonNull-annotation, where your custom annotation wouldn't even be processed, is, when your type doesn't contain any other Lombok annotation, e.g.:
class NoLombokAnnotationsAnywhere {
void testMe(#lombok.NonNull String nonNull) { /* .. */ }
}
will produce a NullPointerException as soon as you call new NoLombokAnnotationsAnywhere().testMe(null). Whereas this wouldn't throw anything with your custom annotation. Of course this only applies as long as you don't have any other Lombok annotations there. As soon as the type gets processed by Lombok, your annotation gets processed too.
If you have your own NonNull-annotation, then you can add just another Lombok-annotation that seems appropriate and Lombok adds a null-check for you, e.g.:
#Data
class NonNullData {
#mycustom.Nonnull
String value;
}
// Calling the following throws a NullPointerException as expected
new NonNullData(null);
You may also find the following issue relevant: Support annotations named #NotNull as well as #NonNull

Getting annotation value of calling method

I want to get a value in annotation from method that is calling another method and that needs to be used with annotation used in called method.
Example
#MyAnnotation(id="method-invoker")
public void invoker(){
executor();
}
#ChildMethod
public void executor(){
}
In above example I want to get value set in id field in #MyAnnotation when handling #ChildMethod annotation.
How can I do this?
First you have to get the stack trace, then extract caller's name from it, then get the Method and get its annotation:
String id = getClass().getMethod(new Throwable().getStackTrace()[1].getMethodName()).getAnnotation(MyAnnotation.class).id();
(obviously it is a bad style to perform so many calls sequentially in one line, but it is ok for example).
But this method is limited. It does not support methods overloading. For example if you have 2 methods called invoker() that have different signature in one class you cannot distinguish between them using pure reflection API.
Fortunately I implemented library named CallStack that can do this: https://github.com/alexradzin/callstack
Using CallStack you can say:
CallStack.getCallStack().getFunction().getAnnotation(MyAnnotation.class).id()

Categories

Resources