Custom `NonNull` annotation in Lombok - java

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

Related

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

Overload lombok setter

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)

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.

Can I aggregate, proxy, or imitate #SuppressWarnings in Java?

I use Spring to build a ModelMap, which is rendered and then parsed by Mustache. For this reason, I have some classes that only exist to wrap data for Mustache's convenience.
These classes have fields and methods that are never used in the code, but are in fact used by Spring (and, in turn, Mustache). I want to explicitly state that this is intentional by annotating the class with a custom annotation #ForMustache that looks like this:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.SOURCE)
public #interface ForMustache {
}
My goal is basically a fancy way of saying #SuppressWarnings("unused") // For Mustache.
I attempted to annotate ForMustache with #SuppressWarnings("unused"), but its effects apply inside my annotation instead of being propagated to the type annotated with #ForMustache.
Can I proxy or imitate #SuppressWarnings("unused") with another annotation?
The #SuppressWarnings annotation is meant for the compiler and other compile time tools that work directly on the source files.
It is also specified as
#Retention(RetentionPolicy.SOURCE)
public #interface SuppressWarnings { ...
meaning it's not supposed to be available anymore at runtime. This means that even springs meta-annotation aware processing can't see it anymore.
Given enough time, you could build an annotation processor that, during compile-time, transforms a custom #ForMustache annotation into a #SuppressWarnings so the compiler can pick it up. In similar fashion Lombok can turn
#Data
class Foo {
private final String value;
}
into a) code that compiles and doesn't complain about no assignment to a final field and b) it wouldn't generate a warning because it generates a public getter for the field. Those can only be assumed to be used / they are usable from places outside the compiler's reach.
Besides putting #SuppressWarnings everywhere, your only chance is to remove the condition that causes the warning.
Disable the check with a compiler option
add public getters or other code so the fields are used (with lombok for example)
hook into the compile process.

Java annotation example in official docs

While reading the Oracle documentation on annotations (quite new to this concept), I came across the following snippet in the beginning (link at the bottom). I am not clear on what the example is illustrating. Is the public #interface definition an enhanced version of a normal interface definition? id(), engineer() etc are methods that return default values if not specified in the interface implementation? But then the instantiation is confusing, is it providing an implementation of an interface where id() returns 2868724 etc? Also not clear what the function travelThroughTime() is for. Any clarifications appreciated:
/**
* Describes the Request-For-Enhancement(RFE) that led
* to the presence of the annotated API element.
*/
public #interface RequestForEnhancement {
int id();
String synopsis();
String engineer() default "[unassigned]";
String date(); default "[unimplemented]";
}
#RequestForEnhancement(
id = 2868724,
synopsis = "Enable time-travel",
engineer = "Mr. Peabody",
date = "4/1/3007"
)
public static void travelThroughTime(Date destination) { ... }
http://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html
To break down your questions:
Is #interface just an enhancement of interface?:
No, #interface is declaring something quite different from a standard interface- you are essentially declaring an annotation type. Making this declaration enables the declared thing to be used as an annotation in other code. So the declaration:
public #interface RequestForEnhancement
enables the annotation #RequestForEnhancement to be used in later code.
An annotation describes metadata for a method or a class. The #RequestForEnhancement annotation, for example, might be placed in front of a method in another class to indicate that some developer wants that method to be changed in some way.
Declaring an interface, by contrast, is declaring the signature of a group of functions. Classes which later implement an interface must then provide implementations of those functions.
What are the "methods" (synopsis(), engineer(), etc.) in the annotation body for? These are not really methods like you would be used to seeing in a class or interface definition. Instead, these represent fields that the annotation you've just declared has. A #RequestForEnhancement annotation on a method should indicate what the requested change to the method is, and possibly who is expected to implement the enhancement to the method. Thus the fields synopsis and engineer are fields that can be included in the annotation.
What does this section mean?:
#RequestForEnhancement(
id = 2868724,
synopsis = "Enable time-travel",
engineer = "Mr. Peabody",
date = "4/1/3007"
)
public static void travelThroughTime(Date destination) { ... }
This is an example of using the annotation that we've declared in the block starting with #RequestForEnhancement. Usages like this will likely occur all over your codebase, in many different classes, once the annotation has been defined. In this particular example, there is a method travelThroughTime(Date destination) in some class which apparently doesn't work very well. Some developer coming across the method thought it should be improved by making it do what it appears to claim to do (travel through time). That developer decided to reflect his request by putting an #RequestForEnhancement annotation on the method with some information about when the request was made, who was expected to make the enhancement, etc.
Sure, but how do you use the contents of an annotation for anything useful? (A question I'll ask for you :-) )
So let's say I want to write a tool which looks through all of my code for methods annotated with #RequestForEnhancement and send an e-mail to the engineers listed in the request, along with information about the annotation and the request for enhancement. How would I get started?
The basic mechanism to find out what methods have an annotation and the way to get values from the annotation is through Java reflection. A tutorial which includes an example of annotations and reflection is here (it's actually a good tutorial on annotations in general).
So sure, you can use reflection to get info out of these annotations, but when would you run a tool to use the info from the annotations? (another one I'll ask for you) Java provides the ability for you to define annotation processors which use annotation information when your code is compiled. Here's what looks like a reasonable tutorial. You can also use the information in your annotations at runtime. If you've ever used JavaFX, for example, you may have noticed that annotations can affect runtime behavior (adding #FXML to a field helps JavaFX fill that field with a value defined in your fxml).

Categories

Resources