views:

537

answers:

5

Hello,

Sample of code:

public class Foo
{
    public class Bar
    {
         public void printMesg(String body)
         {
             System.out.println(body);
         }
    }
    public static void main(String[] args)
    {
         // Creating new instance of 'Bar' using Class.forname - how?
    }        
}

Is it possible to create new instance of class Bar giving its name? I tried to use:

Class c = Class.forName("Foo$Bar")

it finds the class, but when i use c.newInstance() it throws InstantiationException.

A: 

Yes. Remember you need to feed the outer instance to an inner class. Use javap to find the constructor. You will need to go through java.lang.reflect.Constructor rather than rely upon the evil Class.newInstance.

Compiled from "Foo.java"
public class Foo$Bar extends java.lang.Object{
    final Foo this$0;
    public Foo$Bar(Foo);
    public void printMesg(java.lang.String);
}

javap -c is interesting on the constructor because (assuming -target 1.4 or later, now implicit) you get an assignment of an instance field before calling the super constructor (used to be illegal).

public Foo$Bar(Foo);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield        #1; //Field this$0:LFoo;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
   9:   return
Tom Hawtin - tackline
i've never heard about javap before. Thx for showing me that nice tool :).
bigfun
+7  A: 

You need to jump through a few hoops to do this. First, you need to use Class.getConstructor() to find the Constructor object you want to invoke:

Returns a Constructor object that reflects the specified public constructor of the class represented by this Class object. The parameterTypes parameter is an array of Class objects that identify the constructor's formal parameter types, in declared order. 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.

And then you use Constructor.newInstance():

If the constructor's declaring class is an inner class in a non-static context, the first argument to the constructor needs to be the enclosing instance

skaffman
That's what i needed. Thanks for complete explanation!
bigfun
A: 

Quick and dirty code:

Foo.Bar.class.getConstructors()[0].newInstance(new Foo());

Explanation: You must tell the Bar about its enclosing Foo.

meriton
It's dirty, but it's short and works as well :). Thx.
bigfun
A: 

Inner classes can indeed not be constructed without constructing the parent class first. It cannot exist outside the parent class. You'll have to pass an instance of the parent class in when doing reflection. Nested classes are static and they can be used independently from the parent class, thus also when doing reflection.

Here's an SSCCE which demonstrates all the stuff.

package mypackage;

import java.lang.reflect.Modifier;

public class Parent {

    public static class Nested {
        public Nested() {
            System.out.println("Nested constructed");
        }
    }

    public class Inner {
        public Inner() {
            System.out.println("Inner constructed");
        }
    }

    public static void main(String... args) throws Exception {
        // Construct nested class the normal way:
        Nested nested = new Nested();

        // Construct inner class the normal way:
        Inner inner = new Parent().new Inner();

        // Construct nested class by reflection:
        Class.forName("mypackage.Parent$Nested").newInstance();

        // Construct inner class by reflection:
        Object parent = Class.forName("mypackage.Parent").newInstance();
        for (Class<?> cls : parent.getClass().getDeclaredClasses()) {
            if (!Modifier.isStatic(cls.getModifiers())) {
                // This is an inner class. Pass the parent class in.
                cls.getDeclaredConstructor(new Class[] { parent.getClass() }).newInstance(new Object[] { parent });
            } else {
                // This is a nested class. You can also use it here as follows:
                cls.getDeclaredConstructor(new Class[] {}).newInstance(new Object[] {});
            }
        }
    }
}

This should produce

Nested constructed
Inner constructed
Nested constructed
Inner constructed
Nested constructed
BalusC
A: 

Other answers have explained how you can to what you want to do.

But I want to suggest to you that the fact that you need to do this at all is an indication that there is something a bit wrong with your system design. I would suggest that you either need a (non-static) factory method on the enclosing class, or you need to declare the inner class as static.

Creating a (non-static) inner class instance reflectively has a "smell" of broken encapsulation.

Stephen C