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?
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 need to create Custom annotation in java to check null value *i.e
#NonNull
public void test(String [] input , String str){
}
How to add the annoation to input and str fields
When creating your Annotation class, you have to add the #Target annotation to mark where this Annotation is valid. For parameters this would be ElementType.PARAMETER.
This minimal example will compile. (The Interface is a private nested class here, but you will want to define it in its own .java file)
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
private static #interface NonNull {
}
public void test(#NonNull String[] input, #NonNull String str) {
}
Note that this does not provide any null-checking, but tells the caller that this parameters may not be null in the function.
I'm pretty sure you can implement some kind of null-checking in the annotation class, but I'm not sure, so that has to be answered by someone else or you can probably look at some existing implementations of #NonNull interfaces.
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.
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 been trying to set the logTime attribute in my annotation in the spring xml. I am seeing that this is not as easy as I first thought.
#Component
#Retention(RetentionPolicy.RUNTIME)
public #interface LogExecTime {
public boolean logTime() default true;
}
I have tried to use the #Value annotation with the interface with no luck:
I)
#Component
#Retention(RetentionPolicy.RUNTIME)
public #interface LogExecTime {
#Value("#{ConfigureAnnotation.doLogging}")
public boolean logTime() default true;
}
and also
II)
#LogExecTime(logTime=#Value("#{ConfigureAnnotation.doLogging}"))
Any ideas how I can do this at xml level or is this not possible with annotation dependency injection?
Yeah -- that's not ever going to work.
#LogExecTime(logTime=#Value("#{ConfigureAnnotation.doLogging}"))
will never even compile. Annotations are not executable code, they're just markers -- extra bit of information that are inserted into the class file whole sale.
You could either put this:
#Value("#{ConfigureAnnotation.doLogging}")
boolean logTime = true;
As a real field on a spring managed bean somewhere, or have change your annotation to be like:
#Component
#Retention(RetentionPolicy.RUNTIME)
public #interface LogExecTime {
public String logTime() default "true";
}
and have whatever is processing that annotation at run time also accept a spring EL expression and resolve it appropriately, and your component would look like this:
#LogExecTime(logTime = "#{ConfigureAnnotation.doLogging}")
public class SomeComponent {
// blah blah blah
}