Modifying final fields in Java
final, java, reflection
Solution
Compile-time constants are inlined (at javac compile-time). See the JLS, in particular 15.28 defines a constant expression and 13.4.9 discusses binary compatibility or final fields and constants.
If you make the field non-final or assign a non-compile time constant, the value is not inlined. For instance:
private final String stringValue = null!=null?"": "42";
Problem
Let's start with a simple test case: ``` import java.lang.reflect.Field; public class Test { private final int primitiveInt = 42; private final Integer wrappedInt = 42; private final String stringValue = "42"; public int getPrimitiveInt() { return this.primitiveInt; } public int getWrappedInt() { return this.wrappedInt; } public String getStringValue() { return this.stringValue; } public void changeField(String name, Object value) throws IllegalAccessException, NoSuchFieldException { Field field = Test.class.getDeclaredField(name); field.setAccessible(true); field.set(this, value); System.out.println("reflection: " + name + " = " + field.get(this)); } public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException { Test test = new Test(); test.changeField("primitiveInt", 84); System.out.println("direct: primitiveInt = " + test.getPrimitiveInt()); test.changeField("wrappedInt", 84); System.out.println("direct: wrappedInt = " + test.getWrappedInt()); test.changeField("stringValue", "84"); System.out.println("direct: stringValue = " + test.getStringValue()); } } ``` Anybody care to guess what will be printed as output (shown at the bottom as to not spoil the surprise immediately). The questions are: - Why do primitive and wrapped integer behave differently? - Why does reflective vs direct access return different results? - The one that plagues me most - why does String behave like primitive `int` and not like `Integer`? Results (java 1.5): ``` reflection: primitiveInt = 84 direct: primitiveInt = 42 reflection: wrappedInt = 84 direct: wrappedInt = 84 reflection: stringValue = 84 direct: stringValue = 42 ```