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
.
Each Java primitive has a corresponding wrapper: boolean, byte, short, char, int, long, float, double. Boolean, Byte, Short, Character, Integer, Long, Float, Double.
The eight primitive data types byte, short, int, long, float, double, char and boolean are not objects, Wrapper classes are used for converting primitive data types into objects, like int to Integer etc.
Generally, choose primitive types over wrapper classes unless using a wrapper class is necessary. Primitive Types will never be slower than Wrapper Objects, however Wrapper Objects have the advantage of being able to be null.
A primitive type is a predefined data type provided by Java. A Wrapper class is used to create an object; therefore, it has a corresponding class. A Primitive type is not an object so it does not belong to a class. The wrapper class objects allow null values.
Here, we have used the valueOf () method of the Wrapper class ( Integer, Double, and Boolean) to convert the primitive types to the objects. To learn about wrapper classes in Java, visit Java Wrapper Class.
Explanation: All primitive wrapper classes (Integer, Byte, Long, Float, Double, Character, Boolean and Short) are immutable in Java, so operations like addition and subtraction create a new object and not modify the old. The below line of code in the modify method is operating on wrapper class Integer, not an int.
Wrapper classes can be used to achieve that task. The difference between wrapper class and primitive type in Java is that wrapper class is used to convert a primitive type to an object and object back to a primitive type while a primitive type is a predefined data type provided by the Java programming language.
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);
Primitive types get inlined.
In fact even non-primitive constants, when imported into other classes, will be copied, and the import forgotten. So there it will not work either. Only for constant caches, like the string pool, and Integer (Integer.valueOf(13)) caches you may overwrite their values.
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