I was trying to change the value of a private static final
field using reflection (yes, that's probably a very bad idea to begin with, I know).
And, well, for the most part it works fine using following code:
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class A {
public static void main(String[] args) throws ReflectiveOperationException {
System.out.println("Before :: " + B.get());
Field field = B.class.getDeclaredField("arr");
field.setAccessible(true);
// System.out.println("Peek :: " + ((String[]) field.get(null))[0]);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, new String[] { "Good bye, World!" });
System.out.println("After :: " + B.get());
}
}
class B {
private static final String[] arr = new String[] { "Hello, World!" };
public static String get() {
return arr[0];
}
}
Which prints as expected:
Before :: Hello, World!
After :: Good bye, World!
Problems arise when I try to get
the field value via reflection prior to set
ting it.
That is, if I uncomment the commented line in above sample, I get the following:
Before :: Hello, World!
Peek :: Hello, World!
Exception in thread "main" java.lang.IllegalAccessException: Can not set static final [Ljava.lang.String; field B.arr to [Ljava.lang.String;
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)
at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.java:77)
at java.lang.reflect.Field.set(Field.java:764)
at A.main(A.java:14)
Why is this happening??
I tried to set again the accessible
flag after the call to get
but it doesn't help.
I tried many other things that don't seem to help either...
Thanks for your help!
Edit: there is an element of answer in Using reflection to change static final File.separatorChar for unit testing? (see "Important update" by @Rogério).
Important update: the above solution does not work in all cases. If the field is made accessible and read through Reflection before it gets reset, an
IllegalAccessException
is thrown. It fails because the Reflection API creates internalFieldAccessor
objects which are cached and reused (see the java.lang.reflect.Field#acquireFieldAccessor(boolean) implementation). Example test code which fails:Field f = File.class.getField("separatorChar"); f.setAccessible(true); f.get(null); // call setFinalStatic as before: throws IllegalAccessException
Sadly, it doesn't say anything about how to work around it... How do I "reset" the field?