Considering the following (failing) JUnit test for Jackson 2.9.5:
package de.azamir.test;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import org.junit.Test;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonFinalTest {
private static final class MyClass {
@JsonProperty("myProperty")
public final String myProperty = "myPropertyValueInit";
}
@Test
public void doTest() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, JsonParseException, JsonMappingException, IOException {
final MyClass deserializedMyClass1 = new ObjectMapper().readValue("{\"myProperty\":\"myPropertyValueDeserialized\"}", MyClass.class);
// "myPropertyValueInit"
final String directValue = deserializedMyClass1.myProperty;
// "myPropertyValueDeserialized"
final String reflectValue = (String) MyClass.class.getDeclaredField("myProperty").get(deserializedMyClass1);
assertEquals(directValue, reflectValue);
}
}
I suspect the Java VM to optimize accesses to final fields such that the value written by Jackson (via reflection) can only be retrieved again via reflection. This just today lead to a very subtle and hard to find bug in our codebase.
Is this a problem that has been addressed in one way or another by Jackson? My gut feeling would be that there is no way for Jackson code do "see" this but who knows.
I know there are multiple ways of improving this code such as
- removing the
@JsonProperty
and disabling final fields as mutators - not initializing the field during declaration but in a constructor
- not making the fields final at all
but is there a recommended way of making sure that no developer in our team (who might not know the caveat) does this?