Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Annotations values provided in dynamic manner

I want to provide annotations with some values generated by some methods.

I tried this so far:

public @interface MyInterface {     String aString(); } 

@MyInterface(aString = MyClass.GENERIC_GENERATED_NAME) public class MyClass {      static final String GENERIC_GENERATED_NAME = MyClass.generateName(MyClass.class);      public static final String generateName(final Class<?> c) {         return c.getClass().getName();     } } 

Thought GENERIC_GENERATED_NAME is static final, it complains that

The value for annotation attribute MyInterface.aString must be a constant expression

So how to achieve this ?

like image 718
thelost Avatar asked May 17 '12 12:05

thelost


People also ask

What is @interface annotation in Java?

@interface is used to create your own (custom) Java annotations. Annotations are defined in their own file, just like a Java class or interface. Here is custom Java annotation example: @interface MyAnnotation { String value(); String name(); int age(); String[] newNames(); }

What is @target annotation in Java?

If an @Target meta-annotation is present, the compiler will enforce the usage restrictions indicated by ElementType enum constants, in line with JLS 9.7. 4. For example, this @Target meta-annotation indicates that the declared type is itself a meta-annotation type.


2 Answers

There is no way to dynamically generate a string used in an annotation. The compiler evaluates annotation metadata for RetentionPolicy.RUNTIME annotations at compile time, but GENERIC_GENERATED_NAME isn't known until runtime. And you can't use generated values for annotations that are RetentionPolicy.SOURCE because they are discarded after compile time, so those generated values would never be known.

like image 192
Tim Pote Avatar answered Sep 21 '22 21:09

Tim Pote


The solution is to use an annotated method instead. Call that method (with reflection) to get the dynamic value.

From the user's perspective we'd have:

@MyInterface public class MyClass {     @MyName     public String generateName() {         return MyClass.class.getName();     } } 

The annotation itself would be defined as

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface @MyName { } 

Implementing the lookup for both of these annotations is rather straight-forward.

// as looked up by @MyInterface Class<?> clazz;  Method[] methods = clazz.getDeclaredMethods(); if (methods.length != 1) {     // error } Method method = methods[0]; if (!method.isAnnotationPresent(MyName.class)) {     // error as well } // This works if the class has a public empty constructor // (otherwise, get constructor & use setAccessible(true)) Object instance = clazz.newInstance(); // the dynamic value is here: String name = (String) method.invoke(instance); 
like image 36
juhoautio Avatar answered Sep 20 '22 21:09

juhoautio