2

I have a list of similar classes that are all children of the same abstract class. I also have an array of Booleans that should correspond to which class should be used.

For example, I have a bunch of classes, named with the following convention:

boolean[] classesOn = new boolean[4];
abstract class myClass {}

class myClass1 extends myClass { public void myClass1(float x, float y) ...}
class myClass2 extends myClass {}
class myClass3 extends myClass {}
class myClass4 extends myClass {}
...

The idea is to only use the classes that have the corresponding boolean on in classesOn. To do this I use a for loop that iterates through classesOn and checks which are true. Right now I have the following code:

for (int i = 0; i < classesOn.length; i++) {
  if (classesOn[i]) {
    switch (i) {
      case 0: c = new myClass1(x, y); break;
      case 1: c = new myClass2(x, y); break;
...

Now this is extremely inefficient and as I add new extensions of myClass I need to add a new case. I'd like to be able to just say if (classesOn[i]) { c = new "myClass" + (i + 1) ();} to create an instance of that particular class.

How can I do this?

(btw, these are just examples, and the actual implementation of each class varies greatly)

The use I'm currently working on is actually in Processing, where there are multiple color schemes each represented in a class. But I'm curious how to do this for all types of classes in the future as well.

The exact code that I'm working on right now looks like this - (but I'm interested in the answer in general)

abstract class Scheme {
  float red,blue,green,x,y;
  String description;
  public void mousespot(){
    this.x = mouseX;
    this.y = mouseY;
    return;
  }
  public float getRed(){
    return this.red;
  }
  public float getBlue(){
    return this.blue;
  }
  public float getGreen(){
    return this.green;
  }
  public String getDescription(){
    fill(255,255,255);
    textSize(32);
    return this.description;
  }
}
class Scheme1 extends Scheme {
  public Scheme1(float x, float y) {
    this.description = "Green-Yellow-GW-Turqouise";
    this.red = map(x, 0, width, 0, 255);
    this.blue = map(y, 0, height, 0, 255);
    this.green = 255 * (float) dist(width/2, height/2, x, y) / (x / y);
  }
}

class Scheme2 extends Scheme {
  public Scheme2(float x, float y) {
    this.description = "Red-Yellow-Peach-Magenta";
    this.green = map(x, 0, width, 0, 255);
    this.blue = map(y, 0, height, 0, 255);
    this.red = 255 * (float) dist(width/2, height/2, x, y) / (x / y);
  }
}

and in the mouseDragged() method:

for (i = 0; i < colorschemesOn.length;i++) {
      if (colorschemesOn[i]) {
        switch(i) {
          case 0: 
            public Scheme selectedScheme = new Scheme1(mouseX,mouseY);
            break;
          case 1: 
            public Scheme selectedScheme = new Scheme2(mouseX,mouseY);
            break;
          case 2:
            public Scheme selectedScheme = new Scheme3(mouseX,mouseY);
            break;
          case 3:
            public Scheme selectedScheme = new Scheme4(mouseX,mouseY);
            break;
          case 4:
            public Scheme selectedScheme = new Scheme5(mouseX,mouseY);
            break;
          case 5:
            public Scheme selectedScheme = new Scheme6(mouseX,mouseY);
            break;
          case 6:
            public Scheme selectedScheme = new Scheme7(mouseX,mouseY);
            break;
          case 7:
            public Scheme selectedScheme = new Scheme8(mouseX,mouseY);
            break;
          case 8:
            public Scheme selectedScheme = new Scheme9(mouseX,mouseY);
            break;
          case 9:
            public Scheme selectedScheme = new Scheme10(mouseX,mouseY);
            break;
          case 10:
            public Scheme selectedScheme = new Scheme11(mouseX,mouseY);
            break;
          case 11:
            public Scheme selectedScheme = new Scheme12(mouseX,mouseY);
            break;
          default:
            public Scheme selectedScheme = new Scheme1(mouseX,mouseY);
            break;
        }
    }
}
marisbest2
  • 1,346
  • 2
  • 17
  • 30
  • Using Reflection you can create an instance by the class name. An example (using a constructor, if it's the default one you can just do `clazz.newInstance()`): http://stackoverflow.com/questions/6094575/creating-an-instance-using-the-class-name-and-calling-constructor By the way, what are you trying to achieve? – MaQy Jun 19 '13 at 22:59
  • It's actually in Processing. I have a program that should have different color schemes and I'm representing each scheme as a class. – marisbest2 Jun 20 '13 at 03:42

2 Answers2

4

Do not rely on naming conventions, create an array rather:

Class<? extends myClass>[] classes = new Class<? extends myClass>[] { 
    myClass1.class, myClass2.class, myClass3.class, myClass4.class
};
boolean[] classesOn = new boolean[classes.length];

Then you can instantiate them with reflection:

if (classesOn[i])  { myClass c = classes[i].getConstructor().newInstance(); }

If your constructor takes arguments:

myClass c;
if (i < classesOn.lenght && classesOn[i])  { 
    c = classes[i]
        .getConstructor(float.class, float.class)
        .newInstance(mouseX, mouseY); 
} else {
    c = new myClass1(mouseX, mouseY);
}
gaborsch
  • 15,408
  • 6
  • 37
  • 48
  • The call to `getConstructor()` is redundant, as `Class#newInstance()` does it implicitly. And I don't think you mean to create an array of the instances? The question asks about creating a single instance of the nth class. – FThompson Jun 19 '13 at 23:07
  • @Vulcan Not really, that's a different apporach. You can catch `InstantiationException` or `InvocationTargetException` with `newinstance()` (if I recall it correctly). You cannot do the same with `Class.newInstance()`. – gaborsch Jun 19 '13 at 23:09
  • I don't think this will help as it will require me to manually extend the class each time. I'm looking to only adjust the number of values in my Boolean array. – marisbest2 Jun 20 '13 at 03:43
  • You have to modify something anyway. Either add a class to the array, or increase the number. It is much more clear if you have a maintained list of classes than just adding 1 to some constant. What if you forget to increase the number, or you do it twice by accident? – gaborsch Jun 20 '13 at 08:07
  • Ok that's fair, if annoying. Changing one number (which would be a CONSTANT in final code) would be easier but whatever... How would I do this with if my constructor takes two arguments (both are floats) – marisbest2 Jun 20 '13 at 14:04
  • Added the code for the two-param constructor to the answer. (`Class.newInstance()` couldn't do this as well). – gaborsch Jun 20 '13 at 14:22
  • Note: If you use `.getConstructor(Float.class, Float.class)` it must be Float and Float. It is more likely the constructor is `(float x, float y)` – Peter Lawrey Jun 21 '13 at 02:06
  • @PeterLawrey Autoboxing should work. If not working, OP can change the constructor `myClass1(float, float)` to `myClass1(Float, Float)` – gaborsch Jun 21 '13 at 11:01
  • @GaborSch Or he can just change the look up to match the signature of the constructor. You really shouldn't use Wrappers when primitives is more correct, not just for performance but for clarity as well. – Peter Lawrey Jun 21 '13 at 11:19
  • @PeterLawrey You were right, it works with primitive types as well. So I changed the code to `getConstructor(float.class, float.class)`. But to call it, JVM will wrap your primitives anyway, you can only pass `Object...`. – gaborsch Jun 25 '13 at 10:21
  • Ya... float.class it was... Still not working properly. I think its a Processing-specific problem though... Some weird behaviors in general... – marisbest2 Jun 27 '13 at 19:24
  • Well, that's not related to the original instantiation problem I think. If you have any specific error message, you could try to find a solution for that. If the behavior "in general" is wrong, try to localize it, and check through your code. – gaborsch Jun 27 '13 at 19:33
2

You can use reflection

 if (classesOn[i]) { c = Class.forName("myClass" + (i + 1)).newInstance(); }

This is rarely a good idea, and there is usually a better way to structure your classes/code which means you don't need it.

A more elegant way might be to use the Reflections library. (not to be confused with the built in one)

It supports querying classes by such things as annotations or interfaces. You could get it to find all the sub classes of myClass no matter what they are called or JAR they are in.

If you constructor takes two arguments you can pass the two arguments to it.

 if (classesOn[i]) { 
     c = Class.forName("myClass" + (i + 1))
              .getConstructor(float.class, float.class)
              .newInstance(mouseX, mouseY); 
 }
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130