JDK-6714327 : Refining the return type of readResolve fools serialization (covariant, serial).
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.io:serialization
  • Affected Version: 5.0
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2008-06-13
  • Updated: 2019-07-16
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.5.0_06"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05)
Java HotSpot(TM) Client VM (build 1.5.0_06-b05, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
If a Serializable class defines a readResolve method; then if it refines the return type, the serialization machinery will not locate the method.

This could be a serious bug if a class is written expecting common behavior, but a test is not done; in other words:

public final class Foo implements Serializable {

    private static final Foo SINGLETON = new Foo();
    private Foo() {}
    private Object readResolve() { return SINGLETON; }

}

may be considered an "obvious or easy" class or a very simple type; but if the readResolve method is made covariant:

    private Foo readResolve() { return SINGLETON; }

then the class will be broken -- serialization will not invoke this readResolve method.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
If a Serializable class defines a readResolve method then it must not refine the return type of the method or the seialization machinery will not invoke the method.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The ability to refine the return type of readResolve.
ACTUAL -
An obscured method signature.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
** There is no indication unless testing is performed.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class SerTest implements Serializable {
    
    private static final SerTest SINGLETON = new SerTest();

    public static void main(String[] args) throws Exception {
        ByteArrayOutputStream b = new ByteArrayOutputStream();
        (new ObjectOutputStream(b)).writeObject(SINGLETON);
        SerTest re
                = (SerTest) (new ObjectInputStream(
                        new ByteArrayInputStream(
                                b.toByteArray()))).readObject();
        System.out.println(SINGLETON);
        System.out.println(re);
        System.out.println(SINGLETON == re);
    }

    private SerTest() { }
    private SerTest readResolve() { return SerTest.SINGLETON; }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
The method cannot be made covariant.  And in truth, there may not be very much utility in refining the return type; but I'll add again that it may be very easy to write a broken implementation that only test coverage could uncover -- there is no checked warning or any other indication.

Comments
EVALUATION The submitter has a fair concern about accidentally declaring a readResolve method with a return type that is a reference type other than Object and it getting consequently ignored by ObjectInputStream. The specification does seem to indicate that a return type of Object is required (even though it might not seem that it should be required), but then again it is not obvious that declaring "throws ObjectStreamException", as indicated by the specification, is in fact optional. Since return type refinement was allowed by the language with J2SE 5.0, one might imagine that ObjectInputStream's invocation of a serializable class's readResolve method is as if there were a supertype-declared readResolve() method returning Object that is being overridden or implemented by the serializable class, and thus its return type could be refined. But ObjectInputStream operates on Java virtual machine classes at runtime, which were not necessarily compiled from valid Java programming language source files, and a JVM class may have multiple methods with the same name and parameter list but different (and entirely unrelated) return types-- so if ObjectInputStream does not require a readResolve's method to return Object, and there are multiple readResolve methods, which one does it invoke? This question gets more complicated when considering that an accessible readResolve method in a superclass can be logically inherited if a serializable class does not declare its own appropriate readResolve method. So, some care would be needed. I actually don't think that the current implementation (which requires the return type to be Object) deals with that sort of case ideally either-- it requires the first readResolve() method returned by Class.getDeclaredMethod on any class to have return type Object, so any other readResolve() method that would be returned by Class.getDeclaredMethods would not be recognized even if its return type is Object.
13-06-2008