8

I would like to understand why the following doesn't work:

public class HelloClass {

    private class MyClass
    {
        public MyClass() 
        {
           System.out.println ("Oh heck this is me!");   
        }
    }

    public Object newInstance (Object o)
    {
        try {
            // java.lang.InstantiationException here
            return o.getClass().newInstance();        
        } catch (Exception e) {
            e.printStackTrace(System.out);
            return null;
        }
    }

    public void run()
    {
        MyClass m = new MyClass();  
        Object o = newInstance(m);
    }

    public static void main(String[] args) 
    {
        HelloClass hd = new HelloClass();
        hd.run();
    }
}

I know of the correct way to achieve this via declaring the newInstance argument as Class<T>, but would like to understand why it cannot be done like the above.

UPD: Here is the exception I'm getting:

java.lang.InstantiationException: HelloClass$MyClass
    at java.lang.Class.newInstance0(Class.java:340)
    at java.lang.Class.newInstance(Class.java:308)
    at HelloClass.newInstance(HelloClass.java:14)
    at HelloClass.run(HelloClass.java:24)
    at HelloClass.main(HelloClass.java:30)
4
  • What class are you trying to instantiate? If it's an abstract class or an interface, then this won't work and you'll get an InstantiationException. Maybe including the stack trace of the exception would help. Commented Feb 6, 2010 at 18:25
  • I am trying to instantiate MyClass, thought it's quite obvious as I'm calling newInstance(m), where m is an instance of MyClass ;). Added the exception to the original post. Commented Feb 6, 2010 at 18:28
  • Why the mention of generics in the title? What do they have to do with this problem? Commented Feb 6, 2010 at 18:40
  • skaffman, see here: java.sun.com/docs/books/tutorial/extra/generics/literals.html Commented Feb 6, 2010 at 18:51

4 Answers 4

6

Constructors to inner classes have a hidden first argument that you need to provide when you use reflection. Pass an instance of the outer class.

Instead of

return o.getClass().newInstance();

use:

return o.getClass().getConstructor(getClass()).newInstance(this);
Sign up to request clarification or add additional context in comments.

2 Comments

And as specifying a particular constructor by position isn't very robust I think I now understand why FindBugs complains in such cases that the class should be a static inner class. Interesting!
Oh yes, like this it's quite nice.
1

The problem seems to be that it's a non-static member class, as it works both if you declare MyClass as static and if you make it a top-level class. Not quite sure why, though.

Comments

1

Very good question!

This is because the inner class has an implicit argument in each constructor - its outer class. Hence it doesn't have a default constructor.

To obtain an instance of it, you must instantiate it by passing the outer class as argument.

 Constructor constructor = o.getClass().getConstructor(HelloClass.class);
 return constructor.newInstance(this);

3 Comments

That's what I thought too, but it seems that newInstance() doesn't have a version that takes an enclosing object, you need to use Constructor objects.
that's what I just saw as well :) fixed it.
Assuming you meant HelloClass.class, this doesn't work for me but throws an IllegalArgumentException: wrong number of arguments.
0

The newInstance method in a Class object only works for a concrete class with a no-arg constructor.

If you change MyClass to be static, it would qualify and the code would work. As it is, it has an implicit constructor with the outer object as a parameter and does not have a no-arg constructor.

You could make your own newInstance method work by using the constructor's newInstance method:

public Object newInstance (Object o)
{
    try {
        final Constructor<? extends Object> constructor =
                     o.getClass().getConstructor(this.getClass());
        return constructor.newInstance(this);
    } catch (Exception e) {
        e.printStackTrace(System.out);
        return null;
    }
}

1 Comment

Only after posting this did I refresh and see that others had given essentially the same answer. I'll leave it anyway, but feel a bit silly!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.