Serializing Java: treat all your fields
All your Java fields belong to us. Reflection is powerful but you have to remember a few rules before dealing with class fields.
This is Serializing Java - emerging series about writing your own serializer in case you didn’t like other serializers.
Don’t let modifiers constrain you
Any private field seems to be not modifiable outside of the owner class. However, it’s as simple as getting a Field and setting it to be accessible:
Field field = obj.class.getDeclaredField("_myPrivateField");
field.setAccessible(true); // without this line, the next one would throw ReflectionException
field.set(obj, newValue);
Oh, and remember final fields? Check this out:
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
// remove final modifier from field
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
Boom! final is mutable.
Mutable Strings
And so, mutable are strings, too! Probably everyone knows why equals() is essential to comparing strings (because comparing references may not work!).
Hey, look mum, that’s soo clever:
static void mutate(String s) throws Exception {
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
value.set(s, s.toUpperCase().toCharArray());
}
Suddenly, you realize that all your String fields were never immutable.
Fields of inner class
Inner class (in Java: non-static class) can be instantiated only in a context of parent class instance.
Too complicated? Let’s have an example:
class Game {
Entity createEntity() {
return new Entity();
}
class Entity {
GlobalId id;
String name;
}
static class GlobalId {
int hash;
Object ref;
}
}
In this case you can do new Game.GlobalId() but you can’t new Game.Entity() outside of Game class. Thanks to that, Entity has access to all fields of parent Game object while GlobalId doesn’t have that connection.
Then there’s the thing - through Reflection we could call getDeclaredFields() for both Entity and GlobalId classes. You’d expect to get 2 fields per class. However, Entity has these fields:
- id
- name
- this$0
What? Let’s filter that (let’s give some Kotlin here):
val fields = ReflectionUtils.getDeclaredFields(type)
.filter { !it.name.equals("this$0") }
Seems simple. But. Noticed that zero at the end of this$0 ? Do you expect to see this$1 ? Yes! The trailing number depends on how deep the inner class is and to which this would refer.
Let’s fix that:
val fields = ReflectionUtils.getDeclaredFields(type)
.filter { !it.name.startsWith("this$") } // cover hidden field in non-static inner class
Summary
Often there is more trouble and possibilites than you expect. Thus, behold the dychotomy of power and lack of full determinism - that’s in terms of available Reflection power and implicit in-memory dependencies.
References
- StackOverflow: How to limit setAccessible to only “legitimate” uses? - interesting discussion about legitimate uses of setAccessible and source of mutable strings
- StackOverflow: Using reflection to change static final File.separatorChar for unit testing? - source of setFinalStatic()
- Thomas Dudziak Weblog: Reflection & inner classes - article from 2005, still up to date