I have a SubClass and a SuperClass, as well as an annotation DocAnnotation. I need a call to SubClass.foo() to get all class annotations from SubClass and SuperClass. The classes are defined like this:
SuperClass
package Code
import Annotations.DocAnnotation;
import java.util.Arrays;
#DocAnnotation("Super Annotation")
public class SuperClass {
public void foo() {
System.out.println(Arrays.toString(this.getClass().getAnnotations()));
System.out.println(Arrays.toString(this.getClass().getDeclaredAnnotations()));
}
}
SubClass
package Code;
import Annotations.DocAnnotation;
#DocAnnotation("Sub Annotation")
public class SubClass extends SuperClass{
}
DocAnnotation
package Annotations;
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Inherited
#Retention(RetentionPolicy.RUNTIME)
#Repeatable(DocAnnotations.class)
public #interface DocAnnotation {
String value();
}
Running SubClass.foo I expect to see both "Super Annotation" and "Sub Annotation" but instead I see only [#Annotations.DocAnnotation(value=Sub Annotation)]. Am I misunderstanding what #inherited does, or am I doing something incorrectly?
Edit:
After adding the annotation #DocAnnotation("Super Annotation") to SubClass (that's the same one as in SuperClass) it actually shows up twice, once for its use in SubClass and once for its use in SuperClass! Now I'm nearly certain I'm misunderstanding something...
This seems to be the intended behavior, or at least it's specified to work this way (doc):
If [...] the user queries the annotation type on a class declaration, and the class declaration has no annotation for this type, then the class's superclass will automatically be queried for the annotation type.
In other words, since SubClass is already annotated with #DocAnnotation, the superclass is not queried.
On further inspection, the behavior seems a bit weird, though, especially after experimenting with presence of the containing annotation type. I came up with the following example which illustrates this (link):
import java.lang.annotation.*;
import java.util.*;
#Inherited
#Repeatable(Container.class)
#Retention(RetentionPolicy.RUNTIME)
#interface Ann {
String value();
}
#Inherited
#Retention(RetentionPolicy.RUNTIME)
#interface Container {
Ann[] value();
}
// Basic case. Result is that
// only #Ann("2") is present on
// ExhibitASub.
#Ann("1")
class ExhibitASuper {
}
#Ann("2")
class ExhibitASub extends ExhibitASuper {
}
// Because this case results in the
// #Container being present on ExhibitBSuper,
// rather than #Ann, all three annotations
// end up appearing on ExhibitBSub.
#Ann("1")
#Ann("2")
class ExhibitBSuper {
}
#Ann("3")
class ExhibitBSub extends ExhibitBSuper {
}
// Similar to the preceding case, by
// forcing the use of #Container, both
// annotations are present on ExhibitCSub.
#Container(#Ann("1"))
class ExhibitCSuper {
}
#Ann("2")
class ExhibitCSub extends ExhibitCSuper {
}
// Yet when we force both to use #Container,
// only #Container(#Ann("2")) is present on
// ExhibitDSub.
#Container(#Ann("1"))
class ExhibitDSuper {
}
#Container(#Ann("2"))
class ExhibitDSub extends ExhibitDSuper {
}
class Main {
public static void main(String[] args) {
for (Class<?> cls : Arrays.asList(ExhibitASub.class,
ExhibitBSub.class,
ExhibitCSub.class,
ExhibitDSub.class)) {
System.out.printf("%s:%n", cls);
for (Annotation ann : cls.getAnnotations()) {
System.out.printf(" %s%n", ann);
}
System.out.println();
}
}
}
The output of which is as follows:
class ExhibitASub:
#Ann(value=2)
class ExhibitBSub:
#Container(value=[#Ann(value=1), #Ann(value=2)])
#Ann(value=3)
class ExhibitCSub:
#Container(value=[#Ann(value=1)])
#Ann(value=2)
class ExhibitDSub:
#Container(value=[#Ann(value=2)])
Note that for B and C we see both the annotations on the superclass and subclass. Presumably this is because (in a strict sense) the annotation present on the superclass is of a different type than the annotation present on the subclass. Note that for D we return to only seeing the subclass annotation because both classes use the container type.
Using the containing annotation type explicitly could be a workaround for some cases, but it's not a general solution because of case D.
I might file a bug report for this tomorrow since this seems pretty undesirable. Since #Inherited predates #Repeatable, this behavior could be from previous versions where this situation couldn't occur.
You are getting this annotation wrong. The javadoc clearly states:
Indicates that an annotation type is automatically inherited. If an Inherited meta-annotation is present on an annotation type declaration, and the user queries the annotation type on a class declaration, and the class declaration has no annotation for this type, then the class's superclass will automatically be queried for the annotation type.
In other words: if you query the subclass, then you would find the super class being annotated. But this thing is not meant for inheritance in the "OO sense". If you want to see both annotations, you have to write code that checks each class in the class inheritance tree manually.
Related
I have the following annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface IdentifiableMethod {
String id() default "";
}
I will have to loop through a list of annotations and for each of them, perform a annotation.id().
Hence, I would have liked to use this "base" annotation to make it extended by other annotations:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface SpecificMethod extends IdentifiableMethod{
//invalid: annotation cannot have extends list
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface OtherSpecificMethod extends IdentifiableMethod{
//invalid: annotation cannot have extends list
}
... and then generically access the .id() method in a loop by getting in parameter a List<A extends IdentifiableMethod>, so that the compiler always makes me access that method.
However, I've just found out that in the Java specification, all Java annotations extend natively the interface Annotation and they cannot have an extends list [Source: this Stack Overflow question and its answers].
Is there any way to reach something similar?
Just to clarify the need, I need to get all the methods of all the classes within my package by reflection and scan for these annotations. They may be different (they may have more or less properties, different usages etc.), but they all need to have a String id field:
List<Class<?>> classes = getClasses(packageName);
for (Class<?> clazz : classes) {
for (Method method : clazz.getMethods()) {
for (Class<A> annotation : annotations) { //<-- annotations is a Collection<Class<A extends Annotation>>
if (method.isAnnotationPresent(annotation)) {
A targetAnnotation = method.getAnnotation(annotation);
String id = targetAnnotation.id(); //<-- this is not valid because A extends Annotation, not IdentifiableMethod
//rest of code (not relevant)
}
}
}
}
P.s. I already did this but I was looking for something cleaner:
String id = targetAnnotation.getClass().getMethod("id").invoke(targetAnnotation).toString();
I have an annotation as:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
String annotationArgument1() default "";
String annotationArgument2();
}
I have two classes as:
class MyClass1 {
#MyAnnotation(annotationArgument1="ABC", annotationArgument2="XYZ")
public void method1(MyClass2 object) {
//do something
}
#MyAnnotation(annotationArgument1="MNO", annotationArgument2="PQR")
public void method2(MyClass2 object) {
//do something
}
}
class MyClass2 {
int num;
}
I want method1 and method2 (or any other method in any other class annotated with #MyAnnotation) to take only one argument as MyClass2 because they are annotated with #MyAnnotation. If some other argument is passed, it must give a compile time error.
Is it actually possible to do this?
If yes, how can it be done and if no, what is alternate to make this kind of behavior possible?
AFAIK, you can use an annotation processor to check the method signature at compile-time.
I recommend to:
consider AbstractProcessor as a base class
consider to use the annotations provide by the javax.annotation.processing package
register the Processor as a service in META-INF/services
package the annotation processor and the annotations in the same jar - together with the registration as a service this will enable the processor whenever your custom annotation processor is used
I have an annotation as:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
String annotationArgument1() default "";
String annotationArgument2();
}
I have two classes as:
class MyClass1 {
#MyAnnotation(annotationArgument1="ABC", annotationArgument2="XYZ")
public void method1(MyClass2 object) {
//do something
}
#MyAnnotation(annotationArgument1="MNO", annotationArgument2="PQR")
public void method2(MyClass2 object) {
//do something
}
}
class MyClass2 {
int num;
}
I want method1 and method2 (or any other method in any other class annotated with #MyAnnotation) to take only one argument as MyClass2 because they are annotated with #MyAnnotation. If some other argument is passed, it must give a compile time error.
Is it actually possible to do this?
If yes, how can it be done and if no, what is alternate to make this kind of behavior possible?
AFAIK, you can use an annotation processor to check the method signature at compile-time.
I recommend to:
consider AbstractProcessor as a base class
consider to use the annotations provide by the javax.annotation.processing package
register the Processor as a service in META-INF/services
package the annotation processor and the annotations in the same jar - together with the registration as a service this will enable the processor whenever your custom annotation processor is used
We have an annotation #Accepts:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Accepts {
Class[] value();
}
It takes a list of Classes. These are later used to validate in a DSL that a field was passed an instance of the classes listed as acceptable.
Some examples of this annotation in use:
public enum PropertyName {
#Accepts({Integer.class})
xCoordinate,
#Accepts({Integer.class})
yCoordinate,
#Accepts({Boolean.class})
showPermission
#Accepts({String.class, FieldScript.class, List.class})
onClick
/* And So On*/
}
I am adding a new item to this enum called 'value' and it can accept a String or a PropertyResolver. PropertyResolver is an interface defined as below:
public interface PropertyResolver<T> {
public T getValue(TagContext tagContext);
}
I don't know how to do a .class on PropertyResolver to pass on to #Accepts. Is it possible to do so?
Thanks.
You will have to do PropertyResolver.class. There will only one Class instance that represents the the class (raw-version).
No such things as PropertyResolver<T>.class or PropertyResolver<Integer>.class exist.
Always, keep in mind that in Java, generics is compile time only feature.
I want to create a custom annotation (using Java) which would accept other annotations as parameter, something like:
public #interface ExclusiveOr {
Annotation[] value();
}
But this causes compiler error "invalid type for annotation member".
Object[] also doesn't work.
Is there a way to do what I want?
The error is produced because you can't use interfaces as annotation values (change it to Comparable and you'll get the same error). From the JLS:
It is a compile-time error if the return type of a method declared in an annotation type is any type other than one of the following: one of the primitive types, String, Class and any invocation of Class, an enum type, an annotation type, or an array of one of the preceding types. It is also a compile-time error if any method declared in an annotation type has a signature that is override-equivalent to that of any public or protected method declared in class Object or in the interface annotation.Annotation.
I'm afraid I don't know of a good workaround, but now at least you know why you get the error.
Depending on the reason why you would want to specify other annotations there are multiple solutions:
An array of instances of a single annotation type
Probably not what you meant in your question, but if you want to specify multiple instances of a single annotation type it's certainly possible:
public #interface Test {
SomeAnnotation[] value();
}
An array of annotation types instead of instances
If you do not need to specify any parameters on the individual annotations you can just user their class objects instead of instances.
public #interface Test {
Class<? extends Annotation>[] value();
}
But an enum would of course also do the trick in most situations.
Use multiple arrays
If the set of possible annotation types you want to use is limited, you can create a separate parameter for each one.
public #interface Test {
SomeAnnotation[] somes() default { };
ThisAnnotation[] thiss() default { };
ThatAnnotation[] thats() default { };
}
Giving a default value to each member makes it possible to only specify arrays for the types you need.
You can do:
Class<? extends Annotation>[] value();
Not sure if that helps, but . . .
I myself hereby propose a workaround for the given problem:
Well, what I wanted to make possible was something like that:
#Contract({
#ExclusiveOr({
#IsType(IAtomicType.class),
#Or({
#IsType(IListType.class),
#IsType(ISetType.class)
})
})
})
Proposed workaround:
Define a class with parameter-less constructor (which will be called by your own annotation processor later) in following way:
final class MyContract extends Contract{
// parameter-less ctor will be handeled by annotation processor
public MyContract(){
super(
new ExclusiveOr(
new IsType(IAtomicType.class),
new Or(
new IsType(IListType.class),
new IsType(ISetType.class)
)
)
);
}
}
usage:
#Contract(MyContract.class)
class MyClass{
// ...
}
I just ran into this exact problem, but (inspired by #ivan_ivanovich_ivanoff) I have discovered a way to specify a bundle of any combination of Annotations as an annotation member: use a prototype / template class.
In this example I define a WhereOr (i.e. a "where clause" for my model annotation) which I need to contain arbitrary Spring meta-annotations (like #Qualifier meta-annotations).
The minor (?) defect in this is the forced dereferencing that separates the implementation of the where clause with the concrete type that it describes.
#Target({})
#Retention(RetentionPolicy.RUNTIME)
public #interface WhereOr {
Class<?>[] value() default {};
}
#Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface JsonModel {
Class<?> value();
WhereOr where() default #WhereOr;
}
public class Prototypes {
#Qualifier("myContext")
#PreAuthorize("hasRole('ROLE_ADMINISTRATOR')")
public static class ExampleAnd {
}
}
#JsonModel(
value = MusicLibrary.class,
where = #WhereOr(Prototypes.ExampleAnd.class)
)
public interface JsonMusicLibrary {
#JsonIgnore
int getMajorVersion();
// ...
}
I will programmatically extract the possible valid configurations from the "where clause" annotation. In this case I also use the prototypes class as a logical AND grouping and the array of classes as the logical OR.