Instantiate one class inside another using a third

Hello again!

I can’t figure out how to use reflection to instantiate one class inside another using a third class. I feel like I’m close…

The goal in this example is to instantiate A.b inside C.

import java.lang.reflect.*;

void setup(){
  A a = new A();
  a.hello();
}

class A {
  String s;
  B b;
  
  A(){
    C c = new C(this);
  }
  
  void hello(){
    println("hello from A!");
    println("s:",s);
    b.hello();
  }
}

class B {
  B(){}
  
  void hello(){
    println("hello from B!");
  }
}

class C {
  
  <T> C ( T obj ){
    
    Class someClass = obj.getClass();
    println("setting fields for instance of class:",someClass.getSimpleName());
    
    Field[] allFields = someClass.getDeclaredFields();
    
    for (Field field : allFields){
      Class type = field.getType();
      print("field",field.getName(),"is type:",type);
      
      switch(type.getSimpleName()){
        
        case "String"    : println(" is a String");
                           try {
                             field.set(obj, "A happy little string");
                           } catch (Exception e) { }
                           break;
                           
        default           : println(" is custom class:",type.getSimpleName());
                            // somehow magically instantiate A.b = new B();
                            try {
                              Class<?> c = Class.forName( type.getName() );
                              println("c:",c);
                              field.set(obj, c);
                            } catch (ClassNotFoundException e){ println(e);
                            } catch (Exception e) { println(e); }
                            break;
      }
    }
  }
}

You’re over-complicating this.

void setup() {
  size(200,200);
  A a = new A();
  a.hello();
}

void draw(){
  background(0);
}

class A {
  String s;
  B b;
  
  A() {
    C c = new C(this);
    c.snakes();
  }

  void hello() {
    println("hello from A!");
    b.hello();
    println(s);
  }
}

class B {
  B() {
  }

  void hello() {
    println("hello from B!");
  }
}

class C {
  A parent;
  C ( A in_a ) {
    parent = in_a;
    parent.b = new B();
  }
  void snakes(){
    parent.s = "Snakes! Argh!";
  }
}

Please don’t make me explain this…

1 Like

Please don’t make me explain this…

I won’t.

My example is simplified for what I’m attempting to do. I do need to use reflection, so your example doesn’t pertain.

(Disclaimer: I’m not familiar with this but researched out of curiosity. What I explain is just gathered from online resources, mostly stackoverflow…)

so the first problem is that

Class<?> c = Class.forName( type.getName() );

is just a Class and not an instance. So usually you have to do this to instantiate:

c.newInstance();

but you will get InstantiationException error, which seems to happen when you don’t have a default constructor. But B does, and here comes the second problem - Processing wraps the code in PApplet, so you need to use .java extension so that the interpreter won’t wrap your code in there.

I ended up with two files, first the main pde file

void setup(){
  A a = new A();
  a.hello();
}

and abc.java

import java.lang.reflect.*;

class A {
  String s;
  B b;
  
  A(){
    C c = new C(this);
  }
  
  void hello(){
    //println("hello from A!");
    //println("s:",s);
    b.hello();
  }
}

class B {
  B(){}
  
  void hello(){
    System.out.println("hello from B!");
  }
}

class C {
  
  <T> C ( T obj ){
    
    Class someClass = obj.getClass();
    //println("setting fields for instance of class:",someClass.getSimpleName());
    
    Field[] allFields = someClass.getDeclaredFields();
    
    for (Field field : allFields){
      Class type = field.getType();
      //print("field",field.getName(),"is type:",type);
      
      switch(type.getSimpleName()){
        
        case "String"    : //println(" is a String");
                           try {
                             field.set(obj, "A happy little string");
                           } catch (Exception e) { }
                           break;
                           
        default           : //println(" is custom class:",type.getSimpleName());
                            // somehow magically instantiate A.b = new B();
                            try {
                              Class<?> c = Class.forName( type.getName() );
                              //println("c:",c.getClass());
                              field.set(obj, c.newInstance());
                            } catch (ClassNotFoundException e){ //println(e);
                            } catch (Exception e) { //println(e);
                          }
                            break;
      }
    }
  }
}

note that in .java file you cannot use Processing specific functions like println!

2 Likes

Thank you for the reply, micaut.

I had forgotten about c.newInstance();

It would seem, then, as you suggest, that this should work:

Class<?> c = Class.forName( type.getName() );
field.set(obj, c.newInstance());

…but it doesn’t… :frowning:

Did you read the second part, which I wrote about .java extension?

Yes… but not very carefully, admittedly.

It may be foolhardy, but I’m not giving up (yet)…

sorry I don’t understand what you mean. I gave you a full answer. Did it work for you?

Hi. Sorry for my confusing answer.

I just got it working. Thank you for your suggestions – it helped.

I forgot that I needed separate tabs for the PApplet inner classes. So I created separate tabs for each A,B,C.

Here’s the setup:

import java.lang.reflect.*;

final String THISPROC = this.getClass().getCanonicalName();
public final PApplet PAPPLET = this;

void setup(){
  A a = new A();
  a.hello();
}

And here’s the switch command inside C:

      switch(type.getSimpleName()){
        
        case "String"    : println(" is a String");
                           try {
                             field.set(obj, "A happy little string");
                           } catch (Exception e) { }
                           break;
                           
        default           : println(" is custom class:",type.getSimpleName());
                            // somehow magically instantiate A.b = new B();
                            try {
                              Class<?> innerClass = Class.forName(THISPROC + "$" + type.getSimpleName() );
                              Constructor<?> ctor = innerClass.getDeclaredConstructor( PAPPLET.getClass() );
                              Object o = ctor.newInstance(PAPPLET);
                              field.set(obj, o);
                            } catch (InstantiationException e) { println(e);
                            } catch (IllegalAccessException e) { println(e);
                            } catch (ClassNotFoundException e){ println(e);
                            } catch (Exception e) { println(e); }
                            break;
      }
    }

I’m getting a ClassNotFoundException from somewhere, and yet I nevertheless get the string hello from B! in stdout.

So it seems to be working, yet I’m a little mystified that it is…

Update1: Oh, I just figured out why I was getting the exception… The class A itself – ie: this$0 – enters the default switch case… I’ll just ignore it so I don’t get the exception…

Update 2:
Class<?> innerClass = Class.forName(someClass.getTypeName().split("\\$")[0] + "$" + type.getSimpleName() );
can be used instead of
Class<?> innerClass = Class.forName(THISPROC + "$" + type.getSimpleName() );
This eliminates the need for the global variable THISPROC (thus making it easier to compile).

1 Like

glad that you figured it out! (and yes, it’s trying to instantiate this as well, which I should have mentioned…)

By the way if anyone else came here, the code can potentially do very wrong things so if you don’t know what you’re doing I advise not to use the code above :slight_smile: In that case perhaps you are misunderstanding something (instead, TfGuy44’s answer is perhaps something you want)