5

It item no 74 of effective java book there is a paragraph (2nd para from last of the item 74) which mentions as per below:

Inner classes (Item 22) should not implement Serializable. They use compiler-generated synthetic fields to store references to enclosing instances and to store values of local variables from enclosing scopes. How these fields correspond to the class definition is unspecified, as are the names of anonymous and local classes. Therefore, the default serialized form of an inner class is ill- defined.

I know about inner class uses compiler generated synthetic field to store reference to enclosing instances e.g. if the enclosing class is MyEnclosing and inner class is MyInner then the enclosing reference is MyEnclosing.this. But i am not able to get the BOLD part. Please help me getting the meaning. Thanks!!!

Trying
  • 14,004
  • 9
  • 70
  • 110

3 Answers3

6

Suppose you have a local class like this:

 class OuterClass {
    Runnable run;

    void method() {
       final int a = 8;
       this.run = new Runnable() {
          public void run() {
             System.out.println(a);
          }
       };
    }
 }

Now suppose I try to serialize this, which contains an object of this inner class type. My compiler names that class OuterClass$1 and gives it a field called val$a. But the exact names to be used in this situation are not part of the compiler's spec. Another compiler might choose to call the inner class OuterClass$method$1. In that case, serializing in one compiled version and deserializing in the other would fail, even though the same source file was used.

(Plus, there's also the problem that an anonymous inner class does not have a no-args constructor. But due to the problem above, even a named inner class cannot reliably serialize)

Russell Zahniser
  • 16,188
  • 39
  • 30
  • thanks for your effort. You basically mean to say is val$a is the variable stored in inner class by compiler which (val$a) is part of the out class. Am I correct? If i am correct then what is the problem is not serializing the inner class. Basically i am not able to get your "But the exact names to be used in this situation are not part of the compiler's spec" part. – Trying Mar 21 '13 at 21:12
  • 1
    I'm pretty sure that I've seen class files with a naming convention that would have called the inner class here `OuterClass$method$1` instead of `OuterClass$1` (although I tried several compilers just now and wasn't able to reproduce that). The point is the the [spec](http://jcp.org/aboutJava/communityprocess/maintenance/JLS/innerclasses.pdf) only requires that the compiler create a unique name by appending some combination of $'s, letters, and numbers after the enclosing class name. And if the class name changed, deserialization would fail. – Russell Zahniser Mar 22 '13 at 02:31
  • I'm a bit confused... Why would your anonymous `Runnable` class get serialized when you're serializing `this` ? Only the fields of your outer class get serialized. And the question actually refers to the inner / local / anonymous class itself being `Serializable`... – Costi Ciudatu Mar 22 '13 at 02:41
  • @Russell Zahniser Thanks. Just need to conform what you are saying about the doubt. As there is no unified spec how the inner class and the member variables should be named, so serializing and deserializing the inner class may fail as the name varies from compiler to compiler. Am i correct? One more thing is it safe if i will make sure that i am using the same version and same vendor compiler? Thanks!!! – Trying Mar 22 '13 at 09:35
  • @Trying: It is safe to serialize a static nested class. It is generally a bad practice to serialize an inner class, especially if it is local or anonymous. If you want to serialize an object which you implemented with lots of references to inner classes, take a look at the section on serialization proxies in the same book. – Russell Zahniser Mar 22 '13 at 14:55
  • @CostiCiudatu: The point was that I assigned `this.run` to refer to an instance of the anonymous class. – Russell Zahniser Mar 22 '13 at 14:56
1

Consider the following code:

public class Main {
    public static void main(String[] args) {
        final int x = Integer.valueOf(args[0]);
        new Object() {
            void print() {
                System.out.println(x);
            }
        }.print();
    }
}

My compiler calls the anonymous inner class Main$1. When I disassemble it, I see that a copy of the value of x from the outer scope is stored in a private field called val$x:

private final int val$x;

This is an example of what the bold part is talking about.

NPE
  • 486,780
  • 108
  • 951
  • 1,012
0

An inner class is a non-static class defined within some other class:

class Outer implements Serializable {
    private String someString;

    class Inner implements Serializable {
        private int someInt;
    }

}

Once you have an instance of the Inner class, when you serialize it, it must have a reference to the outer class (which it accesses internally via the Outer.this reference) and how this is achieved for a serialized object is unspecified. The same applies to a local class:

class Outer implements Serializable {
    private String someString;

    Serializable method(final int i) {
        class Inner implements Serializable {
            Inner() {
                System.out.println(i);
            }
        }
        return new Inner();
    }
}

If you serialize the value returned by method(), it would need to have a reference to i, but that's not reliable.

Costi Ciudatu
  • 37,042
  • 7
  • 56
  • 92