0

this is a hard one for me (mostly because i'm not even sure what i'm looking for so ill try to explain the best i can). I want to have a few Engine class files (all will have the same methods but will use different calculations in them). then i want my main class to get the name of the class i choose and call the method inside the correct class. putting into pretty code something like this:

Engine1.java:

public static class Engine1{
    doCoolStuff(){
        //coolStuff happening
    }
}

Engine2.java:

public static class Engine2{
    doCoolStuff(){
        //coolStuff happening in a different way
    }
}

Main.java:

String EngineType = "Engine1";

public class Main{
    public Main(){
        (Class.forName(EngineType)).doCoolStuff();
    }
}

this is pretty much the idea, im not even sure if thats the best way to do it but thats how far i've got for now, im also opened for suggestions if there's a better way for these kind of stuff in Java since im a little bit of a newbie there. obviously at the current state this code doesnt compile (because of the line: Class.forName(...).doCoolStuff(); "cannot resolve method"

EDIT :

this question has been answered already but i felt like its important to note that my code had another issue : the method Class.forName() required the FULL pathname of the class i.e not just "Engine1" but "com.packageName.Engine1". might save some people another 30 min :P

Liran Cohen
  • 1,190
  • 1
  • 9
  • 16
  • Already similar question asked [here](http://stackoverflow.com/questions/1268817/create-new-class-from-a-variable-in-java) – Kumar Oct 31 '14 at 10:12
  • You need to access the method first, see here : https://stackoverflow.com/questions/2467544/invoking-a-static-method-using-reflection. That being said, this is only usefull if your Main class gets the name of the Engine class at runtime (so your example is a bit confusing.) – phtrivier Oct 31 '14 at 10:15

2 Answers2

4

Code to interfaces so all implementations share a method:

public interface Engine {
  public void doCoolStuff();
}

public class Engine1 implements Engine {
  @Override
  public void doCoolStuff() {
    // ...
  }
}

Then, you can call the method with:

((Engine) Class.forName("Engine1").newInstance()).doCoolStuff();

A better way though would be to put instances in a Map:

Map<String, Engine> engines = new HashMap<>();
engines.put("Engine1", new Engine1()); // etc...
// .... later
engines.get("Engine1").doCoolStuff();
BarrySW19
  • 3,759
  • 12
  • 26
  • maybe you want to use "engines.put(Engine1.class.getSimpleName(), new Engine1());" or a wrapper method "public void register(Engine engine) { engines.put(engine.getClass().getSimpleName(), engine); } – Arne Deutsch Oct 31 '14 at 10:31
  • @BarrySw19 Thumbs up for an elegant solution, it does exactly what i want it to do. never thought of having an interface, glad to see its an option. – Liran Cohen Oct 31 '14 at 11:00
0

You can get the method from a class using the getMethod(methodname, parameter types) method of Class. Thus you could use

(Class.forName(EngineType)).getMethod("doCoolStuff").invoke(null);

Note that this only works if doCoolStuff() is a static method, otherwise you'd have to pass an instance of the class to invoke.

Another approach (as already suggested by Barry) would be to use an interface for this and create an instance of the class, e.g. by calling

((Engine)(Class.forName(EngineType)).newInstance()).doCoolStuff();

This assumes you have a no-argument constructor and definitely need to access the class by name. If you just can use the class itself while coding, you should not not use Class.forName() and instead just create a new instance of the class or, if it really must be a static method (although I currently can't think of a reason why) directly call getMethod() on that class.

So the best approach would be (if you can take that route):

interface Engine { void doCoolStuff(); }
class Engine1 implements Engine { void doCoolStuff() { ... } }
class Engine2 implements Engine { void doCoolStuff() { ... } }

And then call it like so:

Engine e = new Engine1(); //if you need to use the class name, call Class.forName(name).newInstance() here
e.doCoolStuff();
Thomas
  • 87,414
  • 12
  • 119
  • 157