13

I've been working on an instantiation method that will allow me to package a variety of similar classes into one outer class. I could then instantiate each unique class type by passing the name of that type to the constructor. After a lot of research and errors, this is what I have come up with. I have left an error, to demonstrate my question.

import java.lang.reflect.Constructor;

public class NewTest
{   
    public static void main(String[] args)
    {

        try
        {
            Class toRun = Class.forName("NewTest$" + args[0]);
            toRun.getConstructor().newInstance();
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
            System.out.println(ex.getMessage());
        }

    }

    public NewTest(){}

    private class one //Does not instantiate
    {
        public one()
        {
            System.out.println("Test1");
        }
    }

    private static class two //Instantiates okay
    {
        public two()
        {
            System.out.println("Test2");
        }
    }
}

Compiling this code and running java NewTest two results in the output Test2, as I intended.

Running java NewTest one results in

java.lang.NoSuchMethodException: NewTest$one.<init>()
        at java.lang.Class.getConstructor(Unknown Source)
        at java.lang.Class.getConstructor(Unknown Source)
        at NewTest.main(NewTest.java:12)

I'm confused about this because, as far as I know, I am referencing an inner class correctly, an outer class should have access to an inner class, and I have a default no arg constructor.

Pshemo
  • 122,468
  • 25
  • 185
  • 269
Jonathon Anderson
  • 1,162
  • 1
  • 8
  • 24
  • One is not static, it needs to be instantiated within an instance of newtest. – MrTux Sep 03 '14 at 00:50
  • So, I would have to do `NewTest parent = new NewTest();` and the `parent.toRun.getConstructor().newInstance()` ? – Jonathon Anderson Sep 03 '14 at 00:51
  • @NonSecwitter No, `parent.toRun` will not be compilable. The special syntax that Java uses on `new` to create an instance of an inner class, does not work on `newInstance`, which just has the syntax of any other method call. – ajb Sep 03 '14 at 01:01
  • The [javadoc for `getConstructor`](http://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getConstructor-java.lang.Class...-) says: "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." Simone's answer uses this info. – ajb Sep 03 '14 at 01:02
  • Ah. I read that, I just didn't understand it until right now. I'm still getting used to the lingo :/ – Jonathon Anderson Sep 03 '14 at 01:04

3 Answers3

26

Non-static inner classes need an instance of the outer class to work properly. So, they don't "really" have a default constructor, they always have a kind of hidden parameter in which they expect an outer class instance.

I don't know why you want to have them all in a single class. If you are doing this so that it's only one file, put them in a package and in separate classes. Having less files does not make your program better.

If instead you need them to share something, so the outer class will work as a kind of "scope", you can still do that without using inner classes, but by passing them a context of some sort.

If you really really want to instantiate the inner class, you need to use the hidden constructor taking the outer class as a parameter :

NewTest outer = new NewTest();
Class<?> toRun = Class.forName("NewTest$" + args[0]);
Constructor<?> ctor = toRun.getDeclaredConstructor(NewTest.class);
ctor.newInstance(outer);
Simone Gianni
  • 11,426
  • 40
  • 49
  • Got it. I think my brain's gone dead, I can't think of the statement at the moment. – Jonathon Anderson Sep 03 '14 at 00:52
  • I need to create a bunch of different button listeners. I just figured it would be neater to make a series of inner classes and select them dynamically. – Jonathon Anderson Sep 03 '14 at 00:58
  • Or would I be better to just have a bunch of listener classes in their own files and call them as needed? – Jonathon Anderson Sep 03 '14 at 01:02
  • Inner classes have the only advantage that they can access the fields of the outer class. That's why they are often used for listeners and stuff like that, because they can modify the state of the main class "from outside", so they are great for callback. If you don't need this, then having separate files will not hurt. – Simone Gianni Sep 03 '14 at 01:05
  • would an inner inner class work, such that I could create a generic `ButtonListener` class that could dynamically load a `HelpButtonListener` or `SubmitButtonListener` and still have access to the outer most fields? Right now I am branching (`if elseif elseif` etc) – Jonathon Anderson Sep 03 '14 at 01:07
  • Or, would I be better off just creating 6 inner classes, one for each button type? – Jonathon Anderson Sep 03 '14 at 01:10
  • hilarious... so internal class instances have a connection site to an instance of its' container class. I imagine this is to be able to access private variables of the class they were declared in. I imagine this was done to make all inner classes act uniformly, even if they are independent of its container's variables. – Dmytro Mar 03 '18 at 23:02
  • @Dmitry yes. If you don't need that connection, then simply declare the inner class "static". Anonymous inner classes (and lambdas, which are nice way of writing anonymous inner classes) always have it. – Simone Gianni Mar 07 '18 at 13:02
5

A non-static inner class cannot be instantiated without an instance of its parent class.

new NewTest().new one()

The call above will successfully instantiate a one.

Your two is being instantiated without an outer instance, because of the static modifier. It is a static nested class.

See the difference between static nested classes and inner classes: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

The111
  • 5,757
  • 4
  • 39
  • 55
0

You need to make class one static. Non-static nested classes must be instantiated with an instance of the containing class.

DeathByTensors
  • 901
  • 1
  • 10
  • 16