I recently stumbled upon Change private static final field using Java reflection and tested polygenelubricants' EverythingIsTrue
class, works fine, System.out.format("Everything is %s", false);
prints Everything is true
indeed. But when I change the code as
public class EverythingIsTrue {
public static final boolean FALSE = false;
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
public static void main(String[] args) throws Exception {
setFinalStatic(EverythingIsTrue.class.getField("FALSE"), true);
System.out.format("Everything is %s", FALSE);
}
}
it prints
Everything is false
Does anybody know why? Does setFinalStatic actually work or not?
You can avoid compiler inlining by making the value a result of a method call, even a dummy one.
public class Main {
// value is not known at compile time, so not inlined
public static final boolean FLAG = Boolean.parseBoolean("false");
static void setFinalStatic(Class clazz, String fieldName, Object newValue) throws NoSuchFieldException, IllegalAccessException {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
Field modifiers = field.getClass().getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
public static void main(String... args) throws Exception {
System.out.printf("Everything is %s%n", FLAG);
setFinalStatic(Main.class, "FLAG", true);
System.out.printf("Everything is %s%n", FLAG);
}
}
prints
Everything is false
Everything is true
When accessing primitive static final fields, the Java compiler will assume that the value is a constant and inline the value instead of generating code that accesses the field. This means that the compiler will replace with the reference to the FALSE
field with the value false
. If you use reflection to access the field, you will see that the value of the field has actually changed.
This will not work for non-primitive fields, as the value of an object reference can not be inlined at compile time.
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