A comment on synthetic accessor method issue for the curious.Recently ADVANCED-JAVA and Eclipse jdt-dev had discussions about a somewhat confusing warning that Eclipse compiler generates when you have a nested class referencing a private outer class field: Write access to enclosing field Outer.s_field2 is emulated by a synthetic accessor method. Increasing its visibility will improve your performance This warning is disabled by default but can be seen when you set Access to a non-accessible member of an enclosing type style setting in the compiler options to anything other than Ignore. As an example, it would be generated for s_field2 but not s_field1 below:public class Outer
{
private static class Nested
{
static void foo ()
{
s_field1 = 1;
s_field2 = -1;
}
} // end of nested class
static int s_field1;
static private int s_field2;
} // end of class
The explanation for this is pretty simple. Static nested and inner classes are compiled into separate .class files (with OuterClassName$NestedClassName.class naming scheme). In fact, as far as the JVM is concerned, they are completely separate classes. The only thing they have in common with their outer parents is they are always in the same package (which is not a lot).Syntactically, nested classes have access to all private internals of their enclosing classes, including private fields. But this does not work at the bytecode level: Nested.foo() cannot reference s_field2 with a direct get/putstatic or get/putfield bytecode instruction. Instead, the compiler generates a package-private accessor method in Nested with a mechanically generated name like access$<some number>. This potentially slows down access and hence you get the warning.public class Outer extends java.lang.Object {
static int s_field1;
private static int s_field2;
public Outer();
static void access$0(int);
private static class Outer. Nested extends java.lang.Object
/* ACC_SUPER bit NOT set */
{
Outer.Nested();
static void foo();
}
Method Outer. Nested()
0 aload_0
1 invokespecial #9
4 return
Method void foo()
0 iconst_1
1 putstatic #20
4 iconst_m1
5 invokestatic #24
8 return
}
Method Outer()
0 aload_0
1 invokespecial #12
4 return
Method void access$0(int)
0 iload_0
1 putstatic #20
4 return
Some people believe that this is not a cause for concern because the generated accessor is still inlinable as most other getters/setters. However, it is not clear why this method is not made final which would have made such an optimization more certain. Furthermore, the synthetic method is package-private and is a potential security loophole. Making the field in question package-private eliminates the issue, in most cases without major design ramifications.Now the esoteric part. Things were not always like that. If you had the same morbid interest in bytecode back in Java 1.1 as I did you might have known that Microsofts compiler supported nested classes without using any synthetic accessors. Here is a javap dump when the same code is compiled using jvc:public class Outer extends java.lang.Object {
static int s_field1;
static int s_field2;
public Outer();
private static class Outer. Nested extends java.lang.Object {
Outer.Nested();
static void foo();
}
Method Outer. Nested()
0 aload_0
1 invokespecial #16
4 return
Method void foo()
0 iconst_1
1 putstatic #20
4 iconst_m1
5 putstatic #23
8 return
}
Method Outer()
0 aload_0
1 invokespecial #12
4 return
There are three differences. #1: no accessor method anymore. Can you see the other differences? s_field2 is now package-private and is being accessed directly (no invokestatic)! Yes, the compiler cheated behind your back and made the field more public that you intended in the source code! Additionally the compiler emits an undocumented field attribute called ActualAccessFlags that (I believe) contains the original access flags for the field. I believe early javac versions (for JDK 1.1.x) did something similar when you used O flag.javap will not show you this attribute. I discovered it because I have my own javap-like tool I use to examine bytecode (I work on a bytecode coverage tool in my spare time and Suns javap just does not cut it in many cases). You will not find any documentation on this attribute anywhere. It is just a Java oddity at this point. ... more constant pool entries ...
[19] CONSTANT_Utf8: I
[20] CONSTANT_Utf8: s_field2
[21] CONSTANT_Utf8: ActualAccessFlags
------------------------------------------------------------------------
number of superinterfaces: 0
------------------------------------------------------------------------
number of declared fields: 2
static s_field1: I
0 attribute(s):
static s_field2: I
1 attribute(s):
ActualAccessFlags [generic]: name @21, length = 8
...
The upshot: I am not sure I understand why Java 2 compilers from Sun and IBM went the synthetic accessor route. They could have used the same old trick of adjusting field access level. Its the same security hole, but the performance wouldnt suffer. I guess the new accessor solution supports separate compilation better.