Java inner classes = compiler magic (or how to create inner class instance using reflection)

Have you ever bean curious how JVM share private data between inner and outer classes?
If yes, then here is some story about some tricks that Java compiler does to support this.

I was recently leading a training of Java generics and prepared some simple exercise to create
generic factory creating instances of generic type using reflection. But not the exercise itself is
interesting but one solution for it. One of the students implemented factory products as
(non static) inner classes in factory. His code at first looked looked like this:

public class Factory<T extends Person> {

    private final Class<T> type;

    public Factory(Class<T> type) {
        this.type = type;
    }

    public T produce()
            throws Exception {
        Constructor<T> constructor = type.getDeclaredConstructor();
        T instance = constructor.newInstance();
        return instance;
    }

    class Child implements Person {

        private Child() {
        }
    }
}

Let’s assume that T is Person and type is Person.class. Everything looks fine. We have default (no argument) constructor in inner class.
It’s private but that’s not a problem for inner class. Everything will work just fine if you try to invoke new Child(); somewhere in Factory.
But we’re trying to do it via reflection on class object. Then it’s not that simple. Let’s look at instance creation once again:

Constructor<T> constructor = type.getDeclaredConstructor();
T instance = constructor.newInstance();

It should be valid for no argument constructor, but when you run it:
Exception in thread “main” java.lang.NoSuchMethodException: getsourcetest.Factory$Child.()
What? I’ve got a no args constructor there! But let’s check javadoc for getDeclaredConstructor() you’ll see that:

If this Class object represents an inner class declared in a non-static context, the formal parameter types include the explicit enclosing instance as the first parameter.

OK. so we add Factory.class to getDeclaredConstructor() call and this to newInstance() call.

Constructor<T> constructor = type.getDeclaredConstructor(Factory.class);
T instance = constructor.newInstance(this);

Should be fine but we get:

Exception in thread “main” java.lang.IllegalAccessException: Class getsourcetest.Factory can not access a member of class getsourcetest.Factory$Child with modifiers “private”

So to just get it working we add setAccessible(true) and we’re done. Final code looks like this:

public T produce()
        throws Exception {
    Constructor<T> constructor = type.getDeclaredConstructor(Factory.class);
    constructor.setAccessible(true);
    T instance = constructor.newInstance(this);
    return instance;
}

That works. And you’re done. But what is interesting here is how it’s done by compiler that you simply call new Child() and it works?
Let’s create new Child the regular way and check what constructors we have declared using reflection API:

new Child();
Constructor<?>[] constructors = Child.class.getDeclaredConstructors();

What we get? We get 2 constructors! One that we declared and another one generated by compiler:
getsourcetest.Factory$Child(getsourcetest.Factory,getsourcetest.Factory$1)
And what is this second (getsourcetest.Factory$1) parameter?
It’s a dummy static inner class. Created by compiler to support accessing private inner class constructor.
You can check that it’s a synthetic constructor by using Modifiers class:

java.lang.reflect.Modifier.isSynthetic(constructors[1].getModifiers());

So that’s how it works. Java compiler generates some synthetic constructors or methods to access private elements of inner/outer classes (you’ll get access$100 for private fields and methods).

Advertisements

One Response to Java inner classes = compiler magic (or how to create inner class instance using reflection)

  1. Pingback: JavaPins

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: