tags:

views:

111

answers:

3

I have the following problem:

1) There is some abstract class A with several anonymous subclasses stored in the static fields of A. There is circular dependency between two of the anonymous subclasses. The code of that abstract class is similar to following:

class A implements Serializable
{ 
    public static final A _1 = new A() {
        public A foo()
        {
            return _2;
        }
    };

    public static final A _2 = new A() {
        public A foo()
        {
            return _1;
        }
    };

    public static final A _3 = new A() {
        public void bar()
        {
            // do something
        }
    };
}

2) Instances of class A is referenced by other objects which are used in serialization. There are some objects which are pre-serialized by developers and then included into release as binary data.

After some refactoring of A class binary names of anonymous subclasses was changed in the release builds. I think this may be due to difference of java compiler versions. From .class files made on my machine I can see that anonymous subclasses of A stored in _1, _2 and _3 fields have names A$1, A$2 and A$3, respectively, but from .class files taken from release build I can see that anonymous subclasses of A stored in _1, _2 and _3 fields have names A$2, A$3 and A$1, respectively. Due to this pre-serialized data became unusable and I need to fix this somehow.

Are there any specifications for java compilers or JVM which will say what binary names I should expect for my anonymous classes? The JLS says that name of anonymous class should be name of enclosing class, "$"-sign and non-empty sequence of digits without setting any constraints on these sequences.

I believe that I shouldn't rely on internal names of anonymous classes, I also know "proper" ways to fix that problem like generating pre-serialized data on the build server. Too bad we don't have much time for this now, so I want to know from where this naming difference comes, so I could fix this issue now.

+1  A: 

The only reason I can guess why it fails is that the new Java version reorders the class names because you reference _2 in _1. That said, I don't think you can rely on the names since Java makes no guarantees in which order it will process fields of a class (and therefore, the sequence in which it will create inner classes).

But I think your problem is somewhere else. What error do you get?

Aaron Digulla
I get "local class incompatible" exception during deserialization of A$1 class. This happens because class definition with name A$1 is different from that was during serialization.
Dmitriy Matveev
In that case, [this article][http://www.informit.com/articles/article.aspx?p=31936] might help.
Aaron Digulla
Yes, this is the difference in compilers. However, I haven't figured it out...
Dmitriy Matveev
+2  A: 

May I dare to challenge some elements ? Hopefully it can be useful to you :

  1. if you want your classes to have a well-known name ... well, anonymous is the contrary of a named class ! ;-)
  2. preserializing and delivering objects as binary data is a dangerous choice, and you got bitten by it (during a refactoring, but I believe that could happen in many other conditions). Serialized data is usually considered as a short term solution in Java, good for a few seconds. Many other options are available for longer term storage.


Now, if asked to solve your short-term problem, the only approach I see is to restore your classes to a state compatible with the previous version. If the different ordering you mention is the only difference, I believe that defining the anonymous classes in the same order as before is worth trying ! Also take care that references should be backwards (to a class earlier in the file), not forward (to a class later in the file).

KLE
I don't follow your comment about backward references? Do you mean problems with static initialiser order? / Non-transient serialisation can be done, but it is very much easier if you put some forethought into it.
Tom Hawtin - tackline
@Tom Yes, the backward references avoids some problems with static initialization. But also, **things append in the order you read them**, which is really important when you are trying to get a precise order :-)
KLE
+1  A: 

Did your compiler not give any warnings?

I believe you can read the data without relying on the anonymous class names in the current code by overriding ObjectInputStream.readClassDescriptor. Replace with a descriptor of a "compatible" class. No guarantees that will work, but may be worth a try if your data is important.

Tom Hawtin - tackline