11 Replies Latest reply: Jan 29, 2010 10:10 AM by 843798 RSS

    Hidden parameters on static inner class constructors

    843798
      Using the quick code
          public static void main (String[] args) {
              printConstructors(Outer.Inner.class);
          }
          public static void printConstructors (Class<?> of) {
              System.out.println("Constructors of "+of.getName());
              for (Constructor<?> c: of.getDeclaredConstructors())
                  System.out.println("\t"+c.toString());
          }
      to examine the constructors of my (admittedly rather complexly declared) inner class, I get some weird results, but only in situ - when I take it out to make a test case to send here, it all works fine.

      The code basically amounts to:
      interface MyInterface<T extends MyInterface<T>> {}
      
      abstract class Outer<T extends MyInterface<T>> implements MyInterface<T> {
          // ...
          static class Inner implements Runnable {
              private Inner () {}
              @Override public void run () {/* ,,, */}
          }
      }
      As expected, as a test case, I get the following output:
      Constructors of Outer$Inner
           private Outer$Inner()
      In Situ, I get a second constructor, out of the blue, with package level accessors:
      Constructors of Outer$Inner
           private Outer$Inner()
           Outer$Inner(Outer$Inner)
      The only other thing is I have annotations on all these (which is, in fact, the reason I'm searching constructors - I'm trying to find if each has a given annotation), though when I put the annotations on my test case, I still don't get anything unusual.

      Does anyone have any idea what is going on? Why am I getting this extra constructor? And why is this extra one the one being called?

      Thanks,
      -Nathan
        • 1. Re: Hidden parameters on static inner class constructors
          DrClap
          The compiler generates those.
          • 2. Re: Hidden parameters on static inner class constructors
            843798
            Ok, I have this down to a nice, small, reproducible case:
            import java.lang.reflect.Constructor;
            
            abstract public class MyClass {
                public static void main (String[] args) {
                    printClassData(MyClass.MyInnerClass.class);
                }
            
                public static void printClassData (Class<?> of) {
                    System.out.println("Printing constructors for "+of.getName());
                    System.out.println("    Constructors:");
                    for (Constructor<?> c: of.getDeclaredConstructors()) {
                        System.out.println("\t"+c.toString());
                    }
                }
            
                public void myMethod () {
                    new MyInnerClass();
                }
            
                private static class MyInnerClass {
                    private MyInnerClass () {}
                }
            }
            Output:
            Printing constructors for MyClass$MyInnerClass
                Constructors:
                 private MyClass$MyInnerClass()
                 MyClass$MyInnerClass(MyClass$MyInnerClass)
            If I comment the call "new MyInnerClass()", the extra constructor goes away.

            I've tried this with both Eclipse and the shipped javac compiler for jdk1.6.0_13 - same result in each, so I don't think it's just a weird compiler. And I don't understand why this extra constructor would be needed.

            In particular, I'm trying to figure out what annotations are attached (with RUNTIME retention) to the current constructor. The annotations I attached to my constructor are there, but the created constructor is being called, so I'm asking on the wrong one, and need some rule by which to find the right one.
            • 3. Re: Hidden parameters on static inner class constructors
              DrClap
              Your Inner class has a private constructor. Normally that would mean that other classes don't have access to that constructor, but because it's a nested class, the class it's nested in does have access to it.

              So to make the Inner class consistent to the JVM, the compiler generates a secret package-private constructor which delegates to the private constructor, and makes any usage of Inner's private constructor refer to the package-private constructor. This way all of the accesses are legal with respect to the normal rules of access modifiers.

              So that's the answer for your subject of curiosity. However I don't have an answer for the actual question, the one about annotations. Sorry about that.
              • 4. Re: Hidden parameters on static inner class constructors
                843798
                More and more interesting...

                If, in MyInnerClass, I add a call "private static MyInnerClass construct () {return new MyInnerClass();}", and use that call, instead of the new operator, in myMethod, no new function is added - even though the two cases should completely identical.
                • 5. Re: Hidden parameters on static inner class constructors
                  843798
                  Ah, that makes sense... hence why the back door constructor works.
                  Thanks.
                  • 6. Re: Hidden parameters on static inner class constructors
                    843798
                    Hm...
                    could this be considered a bug, that the annotations on a constructor aren't copied onto artificially constructed constructors?
                    • 7. Re: Hidden parameters on static inner class constructors
                      796085
                      The synthetic constructor should simply delegate to the constructor you wrote. It's not clear what you mean when you say "annotations on the +current constructor+". What is the current constructor?
                      • 8. Re: Hidden parameters on static inner class constructors
                        jtahlborn
                        NathanK wrote:
                        If, in MyInnerClass, I add a call "private static MyInnerClass construct () {return new MyInnerClass();}",
                        or you could just make the constructor you wrote package private instead of private.
                        • 9. Re: Hidden parameters on static inner class constructors
                          843798
                          The synthetic constructor should simply delegate to the constructor you wrote. It's not clear what you mean when you say "annotations on the current constructor". What is the current constructor?
                          This is true with methods. Empirically, it does not seem to be true with constructors. When I declare an inner class constructor private, and construct it from the containing class, I get a call to the synthetic constructor (which has no annotations), but never to the one I wrote (which does).

                          What I mean by "the current constructor" is the constructor being called at the moment when I'm doing my tests.
                          • 10. Re: Hidden parameters on static inner class constructors
                            796085
                            I'm confused. You must get a call to the constructor you wrote. Otherwise the language would be broken.
                            public class Outer {
                                public void blah() {
                                    new Inner();
                                }
                            
                                private class Inner {
                                    private Inner() {
                                        System.out.println("my inner ctor");
                                    }
                                }
                            }
                            Even if a synthetic constructor is generated for visibility reasons, it should delegate to your private constructor.
                            • 11. Re: Hidden parameters on static inner class constructors
                              843798
                              You/re right... it appears my instrumentation is being skipped in some of these cases. Thanks. I had been assuming it was copying instead of referencing code in the case of constructors.