I have a situation where I have to change java constant.
I have below code working
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Main {
public static final Integer FLAG = 44;
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", 33);
System.out.printf("Everything is %s%n", FLAG);
}
}
If I run above , I get following output:
Everything is 44
Everything is 33
But if I change FLAG
variable to int i.e.
public static final int FLAG = 44;
It does not work. The output is :
Everything is 44
Everything is 44
Is there any other way to make it work with Primitive
Type int
.
From jls-4.12.4
A variable of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28), is called a
constant variable
.
Also section 13.1 says (emphasis mine)
3..References to fields that are constant variables (§4.12.4) are resolved at compile time to the constant value that is denoted. No reference to such a field should be present in the code in a binary file (except in the class or interface containing the field, which will have code to initialize it). Such a field must always appear to have been initialized (§12.4.2); the default initial value for the type of such a field must never be observed.
It means that compile-time constant expression from constant variables will be put directly in code by compiler (it will be inlined) not read from final reference at runtime.
For instance if you execute main
method from Bar
class
class Foo{
static{
System.out.println("test if class will be loaded");
}
public static final int x = 42;
}
class Bar{
public static void main(String [] args){
System.out.println(Foo.x);
}
}
you will see no output from static block of Foo
class which means Foo
class hasn't been loaded, which means that value of Foo.x
didn't come from this class. In fact Bar
was compiled this way
class Bar{
public static void main(String [] args){
System.out.println(42); // reference Foo.x will be removed by compiler
// and replaced with actual value because
// compiler assumes that value can't/shouldn't
// change at runtime
}
}
So even changing value of Foo.x
at runtime will not affect value printed in main
method in Bar
class.
You can't change that mechanism.
Possible way around would be initializing your final fields with values created at runtime time (they wouldn't exist at compile time, which will prevent them from being compile-time constant expressions). So instead of
public static final String x = "foo";
try
public static final String x = new String("foo");
or in case of primitive types use Unboxing like instead of
public static final int x = 42;
use
public static final int x = new Integer(42);