The #Parameters annotation implementation from org.testng.annotations looks like this:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE})
public #interface Parameters {
String[] value() default {};
}
So, it should allow me to use it on a ElementType.TYPE => it could also be used on a class.
When I use it on a method, I simply take the value using:
#Parameters("value")
public void m(String value) {
...
}
But if I use
#Parameters("value")
public class A {
...
}
how can I get the value inside the class?
If you want to use it for initialising class variables you can put in on constructor of class and use it.
ElementType.TYPE also means applicable to interfaces and enums - may be that one is specified if you want to extend the annotation.
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();
My question is related to Java: Annotated Annotations (and passing values), but not entirely the same, so I thought I'd ask anyway. Especially since there were so few answers to that question.
Say I have written an annotation like this:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE})
public #interface NestedAnnotation {
public String value();
public Class<?> impl() default Void.class;
}
So if I want to use this, I have to do something like #NestedAnnotation("somevalue"). Now, what if I want to put that annotation inside another one:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#NestedAnnotation("need value here!")
public #interface OuterAnnotation {
public String value();
public Class<?> impl() default Void.class;
}
The NestedAnnotation needs a value, and adding a String (like above) works. But what if I wanted to pass on a value that was received by the OuterAnnotation? Is that possible?
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 have written below the Custom Annotation.
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
String value();
}
and am using the annotation as below.
#MyAnnotation("someValue")
public void someMethod(){
}
above code is working fine without any issues.
But in the annotation class, value() method name i have to reanme. Can i do as below?
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
String name();
}
I tried doing but eclipse is giving the compilation error.
- The attribute value is undefined for the annotation type
MyAnnotation
- The annotation #MyAnnotation must define the attribute
name
Any reason?
Use it like this :
#MyAnnotation(name="someValue")
public void someMethod(){
}
because by default annotation has value method so if you specify like this
#MyAnnotation("someValue")
public void someMethod(){
}
it will by default take it as value="someValue"
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.