JDK-4243945 : javac of Kestrel-F produces incompatible anonymous classes
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 1.3.0
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: solaris_2.5.1
  • CPU: sparc
  • Submitted: 1999-06-04
  • Updated: 2016-07-07
  • Resolved: 2001-01-29
Related Reports
Relates :  
Relates :  
Description

Name: avC70361			Date: 06/04/99



    The javac of Kestrel-F produces incompatible with previous versions of javac.
The javac produces an inner class(with a reference to outer class) for every anonymous
class defined in non-static method. But the previous javac generates the inner class
only when the reference to the outer class is explicitly needed. Moreover, new compiler
doesn't mark the anonymous class final, whereas the older one does. All that can cause
problems with icompatibility of serialization. First, with the new compiler when serializing
anonymous class the outer class is serialized too, but with previous compiler it isn't.
The serialVersionUID will be different due to the additional non-transient field, and
due to the absent final mofifier.

Here is a test demonstrating the bug.
-------------AnonymousTest.java-----------
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class AnonymousTest {
    public static void main(String[] args) {
        AnonymousTest t = new AnonymousTest();

        try {
            ObjectOutputStream os = new ObjectOutputStream(
                new ByteArrayOutputStream()
            );
            os.writeObject(t.createObject());
            System.out.println("Passed");
        } catch (IOException e) {
            System.out.println("Failed. Unexpected exception : " + e);
        }
    }

    public Object createObject() {
        return new Serializable() {
            void method1() {
            }
        };
    }

    public static Object createObject2() {
        return new Serializable() {
            void method2() {
            }
        };
    }
}

-------------------------

1. javap output:

  1.1. jdk 1.2:

    <avv@mizar(pts/3).296> java -version
    java version "1.2"
    Classic VM (build JDK-1.2-V, green threads, sunwjit)
    <avv@mizar(pts/3).297> javac AnonymousTest.java
    <avv@mizar(pts/3).298> javap -private "AnonymousTest\$1"
    Compiled from AnonymousTest.java
    final class AnonymousTest$1 extends java.lang.Object implements java.io.Serializable {
  //^^^^^^^^^^^
        AnonymousTest$1();
        void method1();
    }
    <avv@mizar(pts/3).299> javap -private "AnonymousTest\$2"
    Compiled from AnonymousTest.java
    final class AnonymousTest$2 extends java.lang.Object implements java.io.Serializable {
  //^^^^^^^^^^^
        AnonymousTest$2();
        void method2();
    }

  1.2. Kestrel:

    <avv@mizar(pts/3).316> java -version
    java version "1.3"
    Classic VM (build JDK-1.3-F, green threads, sunwjit)
    <avv@mizar(pts/3).317> javac AnonymousTest.java
    <avv@mizar(pts/3).318> javap -private "AnonymousTest\$1"
    Compiled from AnonymousTest.java
    class AnonymousTest$1 extends java.lang.Object implements java.io.Serializable {
  //^^^^^
        private final AnonymousTest this$0;
      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        AnonymousTest$1(AnonymousTest);
        void method1();
    }
    <avv@mizar(pts/3).319> javap -private "AnonymousTest\$2"
    Compiled from AnonymousTest.java
    class AnonymousTest$2 extends java.lang.Object implements java.io.Serializable {
  //^^^^^
        AnonymousTest$2();
        void method2();
    }

2. serialver output:

  2.1. jdk 1.2:

    <avv@mizar(pts/3).327> java -version           
    java version "1.2"
    Classic VM (build JDK-1.2-V, green threads, sunwjit)
    <avv@mizar(pts/3).328> javac AnonymousTest.java
    <avv@mizar(pts/3).329> serialver "AnonymousTest\$1" 
    AnonymousTest$1:    static final long serialVersionUID = 1679244602498112304L;
                                                           //^^^^^^^^^^^^^^^^^^^^^
    <avv@mizar(pts/3).330> serialver "AnonymousTest\$2" 
    AnonymousTest$2:    static final long serialVersionUID = 7417134557284076014L;
                                                           //^^^^^^^^^^^^^^^^^^^^^

  2.2 Kestrel:

    <avv@mizar(pts/3).332> java -version
    java version "1.3"
    Classic VM (build JDK-1.3-F, green threads, sunwjit)
    <avv@mizar(pts/3).333> javac AnonymousTest.java
    <avv@mizar(pts/3).334> serialver "AnonymousTest\$1" 
    AnonymousTest$1:    static final long serialVersionUID = -3679484467859067554L;
                                                           //^^^^^^^^^^^^^^^^^^^^^^
    <avv@mizar(pts/3).335> serialver "AnonymousTest\$2" 
    AnonymousTest$2:    static final long serialVersionUID = -2682274742240042661L;
                                                           //^^^^^^^^^^^^^^^^^^^^^^

3. Test output:

  3.1. jdk 1.2:

    <avv@mizar(pts/3).339> java -version
    java version "1.2"
    Classic VM (build JDK-1.2-V, green threads, sunwjit)
    <avv@mizar(pts/3).340> javac AnonymousTest.java
    <avv@mizar(pts/3).341> java AnonymousTest
    Passed

  3.2. Kestrel:

    <avv@mizar(pts/3).343> java -version
    java version "1.3"
    Classic VM (build JDK-1.3-F, green threads, sunwjit)
    <avv@mizar(pts/3).344> javac AnonymousTest.java
    <avv@mizar(pts/3).345> java AnonymousTest
    Failed. Unexpected exception : java.io.NotSerializableException: AnonymousTest

======================================================================

Comments
WORK AROUND Name: avC70361 Date: 06/04/99 Replace the anonymous class definition in a non-static method, with a static method call, the last returning the anonymous class instance. The serialVersionUID problem for static nested classes can be avoided by explicitly defining the desired serialVersionUID. Example: Code void method() { ... Applet applet = new Applet() { String getAppletInfo() { return "Applet"; } }; .... } replace with static Applet createApplet() { final static long serialVersionUID = ....; return new Applet() { String getAppletInfo() { return "Applet"; } }; } void method() { ... Applet applet = createApplet(); ... } ======================================================================
11-06-2004

EVALUATION While I think it would be desirable from a minor aesthetic/code-quality point of view to implement both fixes described here, neither is required by the specification. The underlying problem is that serialization is not 'inner-class aware', and improperly computes the serialVersionUID in the presence of the synthetic members generated for inner classes, which do not appear in the source code and which are not tightly specified. Similar problems arise in conjunction with access methods generated to permit access to private members of an, e.g., enclosing class. Other compilers implemented to the spec may behave as ours currently does. See specification bugs 4169272 and 4211551. The suggested workaround, using an explicit serialVersionUID, is the recommended practice pending an opportunity to correct the underlying serialization issue. william.maddox@Eng 1999-06-04
04-06-1999