I have a custom annotation with a single variable.
I use it to annotate attributes in a class and what i need is that the annotation default value for the variable, be the type of the attribute declared. Here the example:
Annotation:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Annotation{
Class<?> className() default ???????; // <- here i need to set something that tells my annotation to take the class of the attribute annotated
}
Class using Annotation:
public class Main {
@Annotation
private AnotherClass annotatedAttribute;
//other code
}
And so what i need is that when i get the annotatedAttribute field and i get its annotation and its value of the className() variable, the default value should be the equivalent to AnotherClass.class unless i state otherwise in the declaration of the @Annotation
E.g:
@Annotation(classname= YetAnotherClass.class)
Is there a way to do this?
I saw some posts talking about an annotation processor, but in my case i don't want to generate new classes files since my class already exist and i'm fetching the field and the annotation through reflection (so i'm at runtime level)
There is no way to specify a custom logic in an annotation, so you have to leave it to the code processing the annotation at runtime, however, you can’t use null
as a marker value either.
The only way to tell your annotation processing tool that custom processing is required, is by choosing a dedicated marker type as default value. This might be a type that would otherwise never occur as a regular annotation value, e.g. void.class
, or you create a class solely for serving as the marker.
To show a similar real life example, JUnit’s @Test
annotation has an expected
element denoting an expected type to be thrown. The default, supposed to express that no exception is expected, can’t be null
nor a type outside the Throwable
hierarchy as the value must conform to the declared type Class<? extends Throwable>
. Therefore, the default value is a dedicated type Test.None
that is never thrown and treated specially by the framework when processing the annotation.
For your case, you have to decide for a suitable marker type or create a dedicated type and adapt the processing code to check for the type. E.g.
public final class UseFieldType {
private UseFieldType() {}
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface YourAnnotation {
Class<?> className() default UseFieldType.class;
}
class YourCodeUsedAtRuntime {
public static Optional<Class<?>> getAnnotationValue(Field f) {
YourAnnotation a = f.getAnnotation(YourAnnotation.class);
if(a == null) return Optional.empty();
Class<?> type = a.className();
if(type == UseFieldType.class) type = f.getType();
return Optional.of(type);
}
}
class Example {
@YourAnnotation String string;
@YourAnnotation(className = Pattern.class) String regEx;
String none;
public static void main(String[] args) {
for(Field f: Example.class.getDeclaredFields()) {
System.out.println(f.getName() + ": "
+ YourCodeUsedAtRuntime.getAnnotationValue(f)
.map(Class::getName).orElse("No annotation"));
}
}
}
string: java.lang.String
regEx: java.util.regex.Pattern
none: No annotation