About Stef Epardaud

Stéphane Épardaud is a long-time open source user and contributor, now working on the Ceylon compiler because if you're not writing a compiler you're not doing proper programming ;)

Java Reflection oddities with inner class constructor parameters

About Java inner classes

Java allows member classes (classes that are defined inside other classes), local classes (classes that are defined inside statement blocks) and anonymous classes (classes with no names):

 
 
 
 
 

class Outer {
    Object anonymous = new Object(){}; // this is an anonymous class
 
    // anonymous initialisation block
    {
        // this is a local class
        class Local{}
        Local l = new Local();
    }
 
    Outer() {
        // this is a local named class in a constructor
        class Local{}
        Local l = new Local();
    }
 
    void method() {
        // this is a local named class in a method
        class Local{}
        Local l = new Local();
    }
 
    // this is a member class
    class Inner{}
    Inner i = new Inner();
}

The Java Language Specification classifies member, local and anonymous classes as inner classes.

Implementation “details”

What the Java Language or Virtual Machine specifications do not tell you is how they are implemented. Some of it is explained already in other articles, such as how the Java compiler generates synthetic methods to allow these members classes access to private fields, which would not be allowed by the JVM.

Another implementation detail of inner classes that is handy to know is that inner class constructors take extra synthetic parameters. It is relatively well-known that the first synthetic parameter of an inner class constructor will be its enclosing instance, which it will store in a this$X synthetic field. This is valid for all three kinds of inner classes: member, local and anonymous.

But it is generally not known that local classes who capture non-constant final variables will require all these variables to be passed as extra synthetic constructor parameters (captured constant final variables will be inlined and not generate extra synthetic constructor parameters):

class Outer {
    void method() {
        final String constant = "foo";
        final String nonConstant = "foo".toUpperCase();
        class Local{
            /* synthetic fields and constructor:
 
            Outer this$0;
            String nonConstant;
 
            Local(Outer this$0, String nonConstant){
                this.this$0 = this$0;
                this.nonConstant = nonConstant;
            }
            */
        }
        Local l = new Local();
    }
}

OK, but why should I care?

In most cases you don’t care, other than for your own curiosity. But if you’re doing Java reflection with inner classes, there are a few things you should know, and because I haven’t found them listed or specified online, I thought it would be important to make a list of things to help others figure it out, because different compilers will produce different results in the Java reflection API.

The question is what happens when you use Java reflection to get a java.lang.reflect.Constructor instance for inner class constructors? In particular, what happens with the methods that allow you to access the parameter types (pre-generics: getParameterTypes()), the generic parameter types (post-generics: getGenericParameterTypes()) and annotations (getParameterAnnotations()), and the answer is: it depends.

Suppose the following Java class:

class Outer {
    class Inner {
        Inner(){}
        Inner(String param){}
        Inner(@Deprecated Integer param){}
    }
}

Here are the size of the arrays returned by these three reflection methods, on each of our constructor, and how they differ depending on the Java compiler used:

Outer.Inner.class
.getDeclaredConstructor()
Outer.Inner.class
.getDeclaredConstructor(
String.class)
Outer.Inner.class
.getDeclaredConstructor(
Integer.class)
getParameterTypes()
.length
122
getGenericParameterTypes()
.length
compiled with Eclipse
122
getGenericParameterTypes()
.length
compiled with Javac
011
getParameterAnnotations()
.length
121

As you can see, the synthetic parameters are always included in getParameterTypes(), but are only included in getGenericParameterTypes() when compiled with Eclipse.

getParameterAnnotations() on the other hand, will always include synthetic parameters except when at least one of your constructor parameters are annotated.

With this info, you now understand the differences between the results of these methods, but so far I still haven’t found a way to determine which parameter is synthetic or not, because although you can make a good guess for the this$X synthetic parameter, which is required by every inner class, you have no way of knowing the number of non-constant captured variables that will end up as synthetic parameters to local class constructors.

 

Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

Leave a Reply


three + = 4



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books