16

I have an Outer class which has a private Inner class.

In my Outer class method, I instantiate the Inner class as follows:

Outer outer = new Outer();
Inner inner = outer.new Inner();

The compiler converts this code to:

Outer outer = new Outer();
Inner inner = new Inner(outer, null);

Using reflection shows that the Inner class has the following synthesized constructors:

private Outer$Inner(Outer)
Outer$Inner(Outer,Outer$Inner)

Since the Inner class is private, the compiler adds that private constructor to it so nobody can instantiate that class. But obviously the Outer class should be able to instantiate it, so the compiler adds that other package private constructor which in turn calls the private constructor. Also, since the package-private constructor has that $ in its name, normal Java code can't call it.

Question: why synthesize one private and one package-private constructor? Why not synthesize only the package-private constructor and be done with it?

shrini1000
  • 7,038
  • 12
  • 59
  • 99
  • @Noofiz these constructors are created by the compiler, without you explicitly coding for them; hence I called them synthesized. – shrini1000 Mar 04 '13 at 10:42
  • @Noofiz If you don't understand the question I suggest you leave it to those who do. – user207421 Mar 04 '13 at 10:43
  • Is the `Outer$Inner(Outer,Outer$Inner)` really correct? The constructor gets an instance of the same class as argument? Why would the compiler add such a parameter. – Philipp Wendler Mar 04 '13 at 11:10
  • @Philipp Wendler it is correct. The compiler overloads it so that it does not conflict with the private constructor signature. Also note that during instantiation, the compiler passes that parameter as 'null' to the constructor (as shown in my code). – shrini1000 Mar 04 '13 at 11:14
  • 1
    Thank you for pointing to my mistake. http://stackoverflow.com/questions/2883181/why-is-an-anonymous-inner-class-containing-nothing-generated-from-this-code - may be duplicate? – Mikhail Mar 08 '13 at 10:59
  • *Also, since the package-private constructor has that $ in its name, normal Java code can't call it.* - this is vastly incorrect. While proper metadata exist, inner classes virtually do not exist as private. They are package private and anything private accessed in the outer class is done via bridge package-private accessor-methods. Lastly you can name all your classes $, $$, $$$ and so on, they are valid names. – bestsss Mar 10 '13 at 18:54

3 Answers3

13

If you write the code like,

public class Outer {
      private class Inner {}
}

You will note that there is only one constructor private Outer$Inner(Outer)

This constructor is required by Section 8.8.9 of the JLS, which says that if no constructor is defined a default constructor must be generated, and in this case the default constructor must be private,

In a class type, if the class is declared public, then the default constructor is implicitly given the access modifier public (§6.6); if the class is declared protected, then the default constructor is implicitly given the access modifier protected (§6.6); if the class is declared private, then the default constructor is implicitly given the access modifier private (§6.6); otherwise, the default constructor has the default access implied by no access modifier.

However, when you you instantiate an instance of Inner inside Outer with code like,

public class Outer {
    private class Inner {}
        public String foo() {
            return new Inner().toString(); 
        }
}

The compiler has to generate a constructor that Outer can legally call (you can't legally call the private default constructor because it is private). So a new synthetic constructor must be generated by the compiler. The new constructor must be synthetic, according to section 13.1 of the JLS

Any constructs introduced by the compiler that do not have a corresponding construct in the source code must be marked as synthetic, except for default constructors and the class initialization method.

This second constructor has no corresponding construct in the source code, so this new constructor must be synthetic. The first private constructor must still be generated, since the JLS requires a private default constructor.

sbridges
  • 24,960
  • 4
  • 64
  • 71
3

This is not an answer, which I think has been well covered by sbridges. It is simply a working example that produces the behaviour you describe:

public class Outer {
    private class Inner {
    }

    public static void main(String[] args) {
        printConstructors();

        //only one constructor is printed but two would appear if you 
        //uncommented the line below

        //new Outer().new Inner();
    }

    private static void printConstructors() {
        Constructor[] constructors = Outer.Inner.class.getDeclaredConstructors();
        for (Constructor c : constructors) {
            System.out.println(c.toGenericString());
        }
    }
}
Community
  • 1
  • 1
assylias
  • 321,522
  • 82
  • 660
  • 783
2

The most likely answer is to respect what you declared in your source code. Doing this still allows to use the private constructor by reflection as you declared it.

This also avoids to check whether the private constructor is actually called within the Inner class.

Didier L
  • 18,905
  • 10
  • 61
  • 103