Why does compiling a class containing static neste

2020-02-23 06:56发布

问题:

In the below code :

 class EnclosingClass
 { 
     public static class BiNode extends Sub.IBiLink { }
     private static class Sub
     {
         private static class IBiLink
         {  
         } 
     }
}

On compiling along with other .class files, I also see a file named "EnclosingClass$1.class" .Why has this been automatically created? Whats going on?

回答1:

First have a look at the class access and propery modifier table from the JVM specifications.

Notice the ACC_SYNTHETIC flag which interpretation specify that it is not present in the source code (in simplier words, it will be added when the class is generated by the compiler).


Let's have a look at the bytecode of EnclosingClass$1.class (note that I will paste only the part that matter).

javap -v EnclosingClass$1.class

produce the following result

Classfile /C:/Users/jfrancoiss/Desktop/Nouveau dossier/EnclosingClass$1.class
  Last modified 2015-03-31; size 190 bytes
  MD5 checksum 5875440f1e7f5ea9a519d02fbec6dc8f
  Compiled from "EnclosingClass.java"
class EnclosingClass$1
  minor version: 0
  major version: 52
  flags: ACC_SUPER, ACC_SYNTHETIC    

Notice that the access flags of the class contains ACC_SYNTHETIC.

The ACC_SYNTHETIC flag indicates that this class or interface was generated by a compiler and does not appear in source code.

An other option to make sure the generated class is synthetic is to compile as

javac -XD-printflat EnclosingClass.java

which would produce

/*synthetic*/ class EnclosingClass$1 {
}

Great, but why generate a synthetic class ?

The Java reflection tutorial can help us understand this. Have a look at the comments in the SyntheticConstructor class

public class SyntheticConstructor {
    private SyntheticConstructor() {}
    class Inner {
    // Compiler will generate a synthetic constructor since
    // SyntheticConstructor() is private.
    Inner() { new SyntheticConstructor(); }
    }
}

So according on the comment, the synthetic class EnclosingClass$1.class was created because IBiLink was private.

Once again, the java reflection tutorial specify at this point

Since the inner class's constructor references the private constructor of the enclosing class, the compiler must generate a package-private constructor.

In our case, we do not see explicitely any constructor call, but we have this line

public static class BiNode extends Sub.IBiLink { }

Let's try compiling this code and see what happen

class EnclosingClass
 { 
     //public static class BiNode extends Sub.IBiLink { }
     private static class Sub
     {
         private static class IBiLink
         {  
         } 
     }
}

No EnclosingClass$1.class generated.


More details noticed when debugging

Change

private static class IBiLink

to

protected static class IBiLink

notice that when compiling, EnclosingClass$1.class is not created.

why does protecting the class did not generate a synthetic class ?

Simply because when protecting the class, you implicitely get access to each of the super classes.


Why don't eclipse compiler generate a synthetic class ?

Eclipse use it built-in compiler, which you can configure it severity level.

By default, Access to a non-accessible member of an enclosing type is set to ignore as you can see on this image.

Change it for example to warning and you will get the following message.

which let me believe that eclipse, altought does not create an other class, will emulate it to simulate the synthetic member.