I have worked with annotation processing on RetentionPolicy.SOURCE level, using javax.annotation.processing.Processor/AbstractProcessor and com.google.auto.service.#AutoService before, that's when I first discovered the very helpful RoundEnvironment.getElementsAnnotatedWith().
Now, in a completely unrelated project, I need the same functionality, but at runtime. In other words, I have the Annotation
#Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Mapper {
Class<?> forUID() default Object.class;
}
and I want to obtain every single element (of course it can only be constructors or methods) anntotated with #Mapper. Can I use RoundEnvironment at runtime? If yes, how do I obtain an instance of it? If no, is there a runtime equivalent to it? Thanks in advance.
Using:
JDK 11 (openjdk11)
Maven 3.6.3
Annotation processing is a compile time only tool, the retention on the annotation is just saying if the annotation will be available at runtime or just in source, for run time you can use reflection, but also you still can use annotation processor at compile time to generate a class that list all the types annotated with a specific annotation.
Related
I have a Java method in which the method itself is annotated with a constraint (cross-parameter) and the arguments are also annotated with constraints (#NotNull, #NotEmpty etc).
Is the method constraint validated after method argument validation or is the order of validation not specified?
Annotations don't inherently do anything. They just mark things. javac itself knows what #Deprecated and #Override and #FunctionalInterface mean, but the effects are always to either do nothing, or to generate a compiler error: These annotations do not cause the compiler to generate any code.
Aside from Project Lombok this is a general principle of annotations, and even Project Lombok is an annotation processor: You have to put lombok on your classpath during a compilation, or nothing happens.
In other words, it is not possible for the #NonNull annotation in your code to generate any null check all on its own. The constraints are applied elsewhere, or by a code generating annotation processor that you explicitly include by putting it on the classpath or passing it along as annotation processor. For example, it IS possible for code you invoke to introspect your method and notice things. Thus, you could for example have:
class Example {
#NotEmpty String name;
}
and then you can do:
new Example("");
and this won't cause an exception. But you could do:
Validator validator = SomeHypotheticalValidationLibrary.newValidator();
validator.validate(new Example(""));
and then this validator would produce an error stating that the instance you provided fails verification. This is an example of annotations being introspected.
And now to answer your question:
The order in which such constraints are validated depends entirely on the validation library you use to do the validation; out of the box, the annotation itself does not and cannot produce any validation code. You'd have to check the documentation of your validation library, and provide the context within which you are validating.
If you're talking specifically about lombok's #NonNull - lombok scans your code for null checks (either of the form if (x == null) throw new Something(); or of the form Objects.nullCheck or guava's nullcheck). If it finds a nullcheck for an #NonNull annotated parameter, lombok does nothing. If it doesn't, it generates a nullcheck after all your explicit null checks. Lombok stops scanning for nullchecks once it hits a line that is NOT a nullcheck (so, neither an if (x == null) nor a methodInvocation(x, "optional text");). #NonNull is currently the only annotation that causes lombok to generate validation code (there is no #lombok.NotEmpty).
We may be able to give more insights if you explain which annotation processors / validation frameworks you are using.
Here's an example of it in the JavaDoc of AnnotationLiteral e.g.
"An instance of an annotation type may be obtained by subclassing AnnotationLiteral."
public abstract class PayByQualifier extends AnnotationLiteral<PayBy> implements PayBy {
}
PayBy paybyCheque = new PayByQualifier() {
public PaymentMethod value() {
return CHEQUE;
}
};
There is a more complete example in Section 5.6.3 in the CDI spec.
5.6.3. Using AnnotationLiteral and TypeLiteral
javax.enterprise.util.AnnotationLiteral makes it easier to specify qualifiers when calling select():
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER, TYPE})
public #interface Synchronous {}
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER, TYPE})
public #interface PayBy {
PaymentMethod value();
#Nonbinding String comment() default "";
}
public PaymentProcessor getSynchronousPaymentProcessor(PaymentMethod paymentMethod) {
class SynchronousQualifier extends AnnotationLiteral<Synchronous>
implements Synchronous {}
class PayByQualifier extends AnnotationLiteral<PayBy>
implements PayBy {
public PaymentMethod value() { return paymentMethod; }
}
return anyPaymentProcessor.select(new SynchronousQualifier(), new PayByQualifier()).get();
}
And finally according to section 9.6 of the Java annotation types spec.:
Unless explicitly modified herein, all of the rules that apply to normal interface declarations apply to annotation type declarations.
For example, annotation types share the same namespace as normal class and interface types; and annotation type declarations are legal wherever interface declarations are legal, and have the same scope and accessibility.
However, the Java compiler complains when I attempt to implement an annotation. In particular, Intellij warns:
"Reports any classes declared as implementing or extending an annotation interface. While it is legal to extend an annotation interfaces, IT IS NEARLY MEANINGLESS, AND DISCOURAGED." (emphasis mine).
Here is the error message as displayed from within Intellij:
The Intellij warning seems to contradict the official Java documentation. I presume the Intellij warning is based upon a warning that comes from the Java compiler. What is correct? The Intellij and/or compiler warning or the documentation?
Annotations types are used as meta data. The typical use case for annotations, at runtime, is with reflection. You annotate something, then you use reflection to retrieve the annotation, process it, and possibly enhance the target. The annotation instances are created and given to you by the JVM through calls to the reflection API.
In that regard, creating your own annotation type instances, which AnnotationLiteral makes easier to do, is kind of pointless since you have no target, since nothing was actually annotated.
It can be useful for cases where you need to mock an annotation type instance or you want to inject some functionality that only exists when processing annotations.
Intellij is simply warning you that it's uncommon.
From Intellij support:
"We can change warning text for example to: "While it is legal to extend an annotation interface it is often done by accident, and the result won't be usable as an annotation." Note that you can always suppress the warning for the statement."
My response:
The proposed solution... would require developers to always suppress the warning for the statement. This solution is poor because the code would be littered with #SuppressWarning or, worse, the developer would need to turn off the warning altogether. The best and most useful solution, would be to not display any warning whatsoever if the developer extends AnnotationLiteral or TypeLiteral and implements an #interface at the same time.
A YouTrack Issue was filed.
I know this #interface element is used to define annotations in Java.
I know annotations were introduced in Java 5.
My questions:
1) how is that element called (formally), and since when
is it recognized by the compiler i.e. when was it introduced?
Is it an annotation or a meta-annotation itself?
2) before Java 8 (e.g. in Java 5 or 6) was there some other element/way
used for defining annotations or had they always been defined that
way ever since their advent in the language?
how is that element called (formally), and since when is it recognized by the compiler i.e. when was it introduced?
An annotation type declaration. This was added in Java 5.
Is it an annotation or a meta-annotation itself?
No, #interface is not an annotation by itself, it's just the keyword interface preceded by an #. It's not a meta-annotation. A meta-annotation is an annotation that can be used on annotation type declarations. The JDK itself has a number of these (for example #RetentionPolicy).
before Java 8 (e.g. in Java 5 or 6) was there some other element/way used for defining annotations
No.
or they had they always been defined that way ever since their advent in the language?
Yes.
Annotations have been added in Java 5 and #interface was always the way to create annotations; there has not been any other mechanism in the past to create annotations.
Why #interface and not a new keyword such as annotation: This was done for backward compatibility. Adding a new keyword means you immediately create a backward compatibility issue, because programs written for the older version might have used the name of the keyword for example as a variable name. Such programs wouldn't compile anymore on the new version. So, when they want to add a new feature to Java, Oracle prefers re-using an existing keyword instead of creating a new one.
That said, new keywords have been added in the course of the evolution of Java, such as enum and assert (which were added in Java 1.4).
I don't know if the question I am asking is really stupid. But here it is:
I would like to write a custom annotation which should be applicable to a specific type. For example, if I have a class A, then I would like to have an annotation that can be applied on objects of A.
Something like this:
#Target({ElementType.FIELD, //WHAT_ELSE_HERE_?})
public #interface MyAnnotation {
String attribute1();
}
public class X {
#MyAnnotation (attribute1="...") //SHOULDN'T BE POSSIBLE
String str;
#MyAnnotation (attribute1="..") //PERFECTLY VALID
A aObj1;
#MyAnnotation (attribute1="...") //SHOULDN'T BE POSSIBLE
B bObj1;
}
Is that possible at all?
Not possible. #Target uses ElementType[], and ElementType is an enum, so you can't modify it. It does not contain a consideration for only specific field types.
You can, however, discard the annotation at runtime, or raise runtime exceptions about it.
That is not possible in Java.
But you have an option to write your own annotation processor if you want to check the correctness of the annotations before runtime.
Annotation processing is a hook in the compile process, to analyse the
source code for user defined annotations and handle then (by producing
compiler errors, compiler warning, emmiting source code, byte code
..).
A basic tutorial on Annotation Processing.
I want to create an annotation that restricts a developer from specifying null as a parameter, which has been annotated with #NoNull
For example, if I create this method:
public void printLine(#NoNull String line) {
System.out.println(line);
}
On a method call, I want an error to appear if the user specifies null for line: printLine(null);
I have been using APT for only a little bit of time, and am wondering how to do this (if possible)?
This is the annotation I have created so far:
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.SOURCE)
public #interface NoNull {}
Compile time will be tough to check, since you're really dealing with runtime values. If you want to create annotations to automatically add code to check this stuff, you should look at project lombok:
http://projectlombok.org/
It uses an annotation processor to add code to your beans to do various things.
For example:
#Getter #Setter
private int id;
The annotation processor would automatically add get/set methods to your bean.
I don't think it has null checks, but you should be able to add this in and contribute it.
Another option is to use the validation jsr, though this requires you to explicitly validate at runtime, but you could accomplish this with proxies or AOP.
#NotNull #Min(1)
public void setId(Integer id)
The point isn't to use the annotation only for readability, but to enforce the annotation at compile-time with APT
Considering that null is a runtime artifact, I don't see how you will enforce a null check at "compile time."
Instead, you'll have to modify your classes, and apt is not the tool to do this, at least not by itself. It exists to extract information about annotated elements from source files. But to enforce your #Null restriction, you need to modify the running class.
One thing that you could do is use apt to extract information about annotated parameters, then use a tool like aspectj to modify those classes at runtime to check the parameter value.
But that's a topic that's way too broad for a single SO question.
#Nullable, #Nonnull are locating in package: javax.annotation
Checkout guava, its got some nice things are type safety:
http://code.google.com/p/guava-libraries/wiki/GuavaExplained