Today I renamed and moved a class in a Java program. It was a Serializable class containing some preferences. As we are getting rid of it I renamed it from Preferences to LegacyPreferences, with the intention of starting a new Preferences class using a regular config file instead of serialization. Bad idea. Java refused to load serialized data into the new class for two reasons: one obvious and expected and one seriously braindamaged in true Java fashion.
The first problem was that the deserialization class could no longer find the class to deserialize to. No wonder since it was in a new package and had a new name. No problem. I subclassed ObjectInputStream and overrode resolveClass(ObjectStreamClass desc) to properly load the class from its new name and location.
That’s when Java freaked out with the following piece of brilliance:
local class name incompatible with stream class name X
Well, duh. Of course the name is different, that’s the point of a refactoring. What exactly were they trying to accomplish when they added this name check? The class has already been found, its interface is an exact match. The name of the class is irrelevant at this point.
I proceeded by overriding readClassDescriptor() with the intention to simply replace the name of the class with the new name when reading the stream. Java would never even realize the class used to have another name. But no: that method must return a java.io.ObjectStreamClass object, which you can’t instantiate or override unless you’re in the java.io package. And if you just grab the super.readClassDescriptor() instance you aren’t allowed to make any changes to it.
At this point I considered my favorite solution to Java damage. I’d like to call it “corrective Java surgery” – use reflection to force your way into the class and fix it. This is a very gratifying solution since you can undo the mistakes in Java and set things truly right. The drawback of course is that different versions of Java may require different corrective code.
That wasn’t necessary in this case since reading the source code of ObjectInputStream revealed a static method for creating ObjectStreamClasses: ObjectStreamClass.lookup. With that tool things fell into place very simply like so:
@Override
protected java.io.ObjectStreamClass readClassDescriptor() throws IOException ,ClassNotFoundException {
ObjectStreamClass desc = super.readClassDescriptor();
if (desc.getName().equals("com.trueship.base.Preferences")) {
return ObjectStreamClass.lookup(LegacyPreferences.class);
}
return desc;
};
Hope that’ll help anyone else looking to rename and deserialize classes in Java.