Community
Participate
Working Groups
If a class (or superclass) provides an implementation of the writeReplace method to properly handle serialized objects, this warning is probably not relevant. And, since it is set to "Warning" by default, it can be annoying to have a bunch of warnings after carefully designing a good strategy for serialization.
It is recommended that each Serializable class has a serialVersionUID field.
I agree, each Serializable class should have a serialVersionUID field. That is, if you want long living code that can be worked with in the future in the largest number of circumstances. I like the reminding warning, becuase is so easy to forget, then you're "hosed" once your code is released. Its easy to make the warning go away, just add the serialVersionUID field. [And ... Ed and I have already argued about this for hours ... so no need to repeat that argument here ... just ran accross this bugzilla so thought I'd chime in with my vote.
David, I think you've missed the point of this defect completely. The point is that if writeReplace is defined, the serialialVersionID isn't used and hence insisting that it's recommended to define it seems pointless at best and pedantic at worst.
Yes, Ed, I did miss the point of this defect. Thanks for calling it to my attention. So, sounds like we've won you over on the Serialization case in general :) and this defect is only for those cases where writeReplace and readResolve are both specified, or accessible from a super class? I agree it would seem to be pointless, if no other versions of that class had ever been released (which would not have had the methods) ... but not so sure about pedantic? :)
Will investigate.
*** Bug 163567 has been marked as a duplicate of this bug. ***
We should also fix the case where a class implements java.io.Externalizable. In this case the serialVersionUID is not required as the class specifies explicitly how it is serialized through the readExternal and writeExternal methods. Since java.io.Externalizable implements java.io.Serializable, we also report that class has having a missing serialVersionUID field.
Also if a Serializable class implements the methods: private void writeObject(java.io.ObjectOutputStream out) throws IOException private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException; then it doesn't seem that they use the serialVersionUID field. This field is only used if the "default" serialization is used. So we should also cover that case.
Not that javac also has such a warning and doesn't make the distinction with the presence of writeExternal, writeObject, readObject or java.io.Externalizable. It seems to check only for serialVersionUID when a class implements java.io.Serializable. Should we be smarter than that?
To summarize what I believe we should do: If any of the following conditions is true, then the class that implements java.io.Serializable doesn't need to provide a serialVersionUID field. 1) it implements the interface java.io.Externalizable. This requires the class to implement the two methods writeExternal and readExternal that are bypassing the default serialization mecanism. 2) it implements the method: ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException; 3) it implements the pair of methods: private void writeObject(java.io.ObjectOutputStream out) throws IOException private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException; Then we would have an accurate warning. Any comment? Am I missing something?
Created attachment 53377 [details] Proposed fix
Created attachment 53379 [details] Regression tests
Released for 3.3M4. Added regression tests in org.eclipse.jdt.core.tests.compiler.regression.SerialVersionUIDTests#test001/006
Verified for 3.3 M4 using build I20061212-0010.
Actually it seems that point 1) is not true: "1) it implements the interface java.io.Externalizable. This requires the class to implement the two methods writeExternal and readExternal that are bypassing the default serialization mecanism." It requires the class to override the data format, but the headers are written by the serialization mechanism just like with default serialization. This also includes the SUID value, which also gets checked upon deserialization. At least that's my impression after hitting a SUID mismatch on an Externalizable class and some poking around the Web :)
Would you have some links about this? My findings in 2006 were that the serial version UID is not used when a class is Externalizable with the corresponding methods implemented. Ed, you had the same conclusion from comment 3.
Well, I don't have definite links, only some grumbling here and there, but I did a quick test. I wrote a trivial class implementing Externalizable, containing a single int, which it writes/reads to/from read/writeExternal(), set SUID to -1 and serialized it to a file. I then changed the SUID of the class to 1 and tried to deserialize it. The result was an exception: Exception in thread "main" java.io.InvalidClassException: sertest.ExternalizableTest; local class incompatible: stream classdesc serialVersionUID = -1, local class serialVersionUID = 1 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:546) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1552) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1466) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1699) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1305) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:348) at sertest.ExternalizableTest.main(ExternalizableTest.java:47) even though SUID is the only thing that changed (apart from some code in main(), but that doesn't matter).
I think this bug should be re-opened, and the fix reverted. The sample code provided by Krzysztof Sobolewski above proves the point I believe. I have hit upon it too.
The documentiaion is highly misleading, but if you are using ObjectInput.readObject the serialVersionUID has to match the one that was written with writeObject. Quick quote from this link: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4094702 "Even through a class is Externalizable, one still has to add a serialVersionUID member to the class"
I second Yoav's request to reopen this bug and revert it to the way it was before. The warning-feature in the current state is nearly useless. No warning is shown for classes that implement Externalizable or that derive from a superclass with a serial version UID already defined. But both scenarios need this to avoid InvalidClassExceptions to occur during deserialization. Alternatively allow the generation of such a UID explicitly instead of just providing that function as quick fix of the compiler warning.