Reputation: 1461
I just wrote this code to test something for a better understanding of reflection.
This is the ReflectionTestMain class:
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionTestMain {
public static void main(String[] args) {
try {
ReflectionTest rt = new ReflectionTest();
Class<ReflectionTest> c = ReflectionTest.class;
Field f = c.getDeclaredField("value");
f.setAccessible(true);
f.set(rt, "text");
Method m = c.getDeclaredMethod("getValue");
m.setAccessible(true);
String value = (String) m.invoke(rt);
System.out.println(value);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
And this is the ReflectionTest class.
public class ReflectionTest {
private final String value = "test";
private String getValue() {
return value;
}
}
This code prints test but I expected it prints text. What is the reason that this does not work and how can I fix that?
Upvotes: 3
Views: 1016
Reputation: 18824
While the variable is properly updated, it isn't propagated to the getValue()
method.
The reason for this is that the compiler optimizes the program for you.
Since the compiler knows that the variable value
is not changed, it compiles it to inline access directly to the string pool, instead of going through the variable. This can be sees by running java -p
on the class file
This can be solved by using a initizer block or constructor for the string constant, or making the statement more complex to "fool" the compiler.
class ReflectionTest {
// Use either
private final String value;
{
value = "test";
}
// Or
private final String value;
public ReflectionTest () {
value = "test";
}
// Or
private final String value = Function.identity().apply("test");
// Or
// Do not replace with + as the compiler is too smart
private final String value = "test".concat("");
// Depending on your required performance/codestyling analyses
private String getValue() {
return value;
}
}
Upvotes: 5
Reputation: 1168
From javadoc on Field.set(..)
* <p>If the underlying field is final, the method throws an
* {@code IllegalAccessException} unless {@code setAccessible(true)}
* has succeeded for this {@code Field} object
* and the field is non-static. Setting a final field in this way
* is meaningful only during deserialization or reconstruction of
* instances of classes with blank final fields, before they are
* made available for access by other parts of a program. Use in
* any other context may have unpredictable effects, including cases
* in which other parts of a program continue to use the original
* value of this field.
So you just use reflection in an incorrect way here.
Upvotes: 1