We are about to switch to a new forum software. Until then we have removed the registration on this forum.
I've previously posted this question, but now that I'm coming to understand java better I'm better able to ask it more fruitfully.
The Problem: I have a string. I want to instantiate a class from it. But I can't.
I've come across many pages that provide what seems to be a fairly straightforward solution, but I cannot get it to work. I'm kind of aware that all the code being executed in a processing session is wrapped in a superclass of some sort, so this could be a knotty reflection problem, but hopefully not an insurmountable one. (Once my class is instantiated I can use reflection to access its fields, so that's good...)
Being able to do this is the crux of my pipeline. That's why, with tail between my legs, I come back seeking the answer again. I do have a workaround using a switch block, but being able to do this with Class.forName(string)
would obviously be far more efficient.
Anyways, without further ado, here is the code. Any help solving what to me is an intractable problem is very appreciated!
import java.lang.reflect.Field;
void setup(){
String thisProc = this.getClass().getCanonicalName();
ArrayList<Model> models = new ArrayList<Model>();
String[] sa = {"Box","Rect"};
for (String m : sa){
Model newModel = null;
/* this is what I want to use, but it doesn't work */
try {
String curClass = thisProc+"."+m; // fully qualified class name
Class clazz = Class.forName(curClass);
newModel = (Model) clazz.newInstance();
println(newModel.getClass());
} catch (ClassNotFoundException e){
e.printStackTrace();
} catch (InstantiationException e){
e.printStackTrace();
} catch (IllegalAccessException e){
e.printStackTrace();
}
/* this is what I currently use -- it works, but it's definitely not optimal */
switch(m){
case "Box":
newModel = new Box();
break;
case "Rect":
newModel = new Rect();
break;
}
models.add(newModel);
}
for (Model m : models){
try {
Field field = m.getClass().getDeclaredField("testVar");
field.set(m, m.getClass().getName());
} catch (NoSuchFieldException e) { println(m.getClass()," has no field testVar");
} catch (Exception e) { throw new IllegalStateException(e); }
m.hello();
}
}
interface Model {
String testVar = "";
void hello();
}
class Box implements Model {
String testVar;
void hello(){ println("hello from",testVar); }
}
class Rect implements Model {
String testVar;
void hello(){ println("hello from",testVar); }
}
Answers
You have two main problems and one surplus constant.
Problems
1) Your 'fully qualified class name' is incorrectly formed
2) Box and Rect are inner classes but the instantiation method used is only suitable for top-level classes.
Surplus constant
In the Model interface the
String testVar = "";
will be treated as a constant so has no link to or relevance to the field with the same name in theBox
andRect
classes. In fact it is only likely to cause confusion so Delete it.So here is the code that works.
OMFG! Thank you!
Were I to sire an heir I would name him Quark.
D'OH! So close yet so far!
If I put the instantiating try block in a different class it seems to fail because it's looking for the constructor in itself, not the outer class...?
Not sure why you are doing this, but always keep in mind the KISS concept.
Below is a suggestion of how your class should look. However, not sure why I am doing this...
Kf
Thank you for the reply, kfrajer.
How do I instantiate the class within
draw()
? I've not usedPApplet
before...You need to think about what this is referring to in your previous code.
SetModels sm = new SetModels(this);
The obejct sm is now aware of the current sketch.
Kf
Yes, of course! Thank you!
Thank you both, quark & kfrajer, very much -- I've been stuck on this for a long time, and now things are working! Life can continue...