Is is possible to change field annotation values at runtime?
I can access the values, but can't find a way to change them.
Access is possible with:
Article.class.declaredFields.find {it.name="annotatedField"}.declaredAnnotations
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.
In Java SE 6, annotations cannot subclass one another, and an annotation is not allowed to extend/implement any interfaces.
Annotations, just like methods or fields, can be inherited between class hierarchies. If an annotation declaration is marked with @Inherited , then a class that extends another class with this annotation can inherit it.
I think it would be best to keep a reference to an Annotation object in addition to your Field (or Object), and update the Annotation reference as you change its values. This way, when the implementation of annotations in Class.java changes, your code is less likely to break.
The answer linked in the question comments is useful for dealing with annotations containing a single element, but if you have multiple elements that you need to set, here is a more general solution that makes use of a proxy:
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) throws Exception {
Foo foo = new Foo();
Field field = foo.getClass().getDeclaredFields()[0];
Anno anno = field.getAnnotation(Anno.class);
System.out.println(String.format("Old properties: %s, %s, %s", anno.value(), anno.bar(), anno.barr()));
Anno anno2 = (Anno) setAttrValue(anno, Anno.class, "value", "new");
System.out.println(String.format("New properties: %s, %s, %s", anno2.value(), anno2.bar(), anno2.barr()));
Anno anno3 = (Anno) setAttrValue(anno2, Anno.class, "bar", "new bar");
System.out.println(String.format("New properties: %s, %s, %s", anno3.value(), anno3.bar(), anno3.barr()));
}
public static Annotation setAttrValue(Annotation anno, Class<? extends Annotation> type, String attrName, Object newValue) throws Exception {
InvocationHandler handler = new AnnotationInvocationHandler(anno, attrName, newValue);
Annotation proxy = (Annotation) Proxy.newProxyInstance(anno.getClass().getClassLoader(), new Class[]{type}, handler);
return proxy;
}
}
class AnnotationInvocationHandler implements InvocationHandler {
private Annotation orig;
private String attrName;
private Object newValue;
public AnnotationInvocationHandler(Annotation orig, String attrName, Object newValue) throws Exception {
this.orig = orig;
this.attrName = attrName;
this.newValue = newValue;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// "override" the return value for the property we want
if (method.getName().equals(attrName) && args == null)
return newValue;
// keep other properties and methods we want like equals() and hashCode()
else {
Class<?>[] paramTypes = toClassArray(args);
return orig.getClass().getMethod(method.getName(), paramTypes).invoke(orig, args);
}
}
private static Class<?>[] toClassArray(Object[] arr) {
if (arr == null)
return null;
Class<?>[] classArr = new Class[arr.length];
for (int i=0; i<arr.length; i++)
classArr[i] = arr[i].getClass();
return classArr;
}
}
class Foo {
@Anno(value="old", bar="bar", barr="barr")
public Object field1;
}
@Retention(RetentionPolicy.RUNTIME)
@interface Anno {
String value();
String bar();
String barr();
}
Program output:
Old properties: old, bar, barr
New properties: new, bar, barr
New properties: new, new bar, barr
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With