简体   繁体   中英

Design Approach and Using Reflection to run methods in Java

I have a question. I have multiple classes in a package: Let's say package is

com.myPackage.first

And this package has the following classes:

firstGood
secondGood
thirdBad
fourthGood

Each of these classes have a method with the same name but different implementation. So say each have a one particular function called:

public void runMe(){

}

For now I want to come up with a way to given a class name, it'll go inside the class and run that particular method.

So conceptually, my method will look like those:

ArrayList<Class> classList ; // where classList is a list of classes I want to run 

public void execute(){
 for(Class c : classList){
  // Go inside that class, (maybe create an intance of that class) and run     the method called run me
 }
}

or

public void execute(Class c, String methodToRun){
 for(Class c : classList){
  // Go inside that class, (maybe create an intance of that class) and    run     the method called run me
 }
}

For now. what I have been able to do is get the name of the classes I want to run the

runMe() 

method. So I have been able to come with a way to get the arraylist of classes I want to run. So what I need help with is coming up with a method such that it takes a class name and run the method I want it to. Any help is appreciated. Thanks

I suggest having a look at Class.forName ( ... ) to get the class object, Class.newInstance(); if your classes have a default constructor (or Class.getDeclaredConstructor(...) otherwise) to create a new instance and then Class.getDeclaredMethod( ... ) to find the method and invoke it.

All of this without any regard if your idea is really a good one, since I really didn't quite understand WHY you want to do what you want to do...

interface Me {
    void runMe();
}

Then let all classes implement Me.

And have a list of Me s

List<Class<Me>> ...

Then

void test(Class<Me> cl) {
    Me me = cl.newInstance();
    me.runMe();
}

My adage is always use reflection to solve a problem - now you have two problems . In view of that have you considered a simple pattern like this:

interface Runner {

    public void runMe();
}

static abstract class BaseRunner implements Runner {

    public BaseRunner() {
        // Automagically register all runners in the RunThem class.
        RunThem.runners.add(this);
    }

}

class FirstGood extends BaseRunner implements Runner {

    @Override
    public void runMe() {
        System.out.println(this.getClass().getSimpleName() + ":runMe");
    }

}

class SecondGood extends BaseRunner implements Runner {

    @Override
    public void runMe() {
        System.out.println(this.getClass().getSimpleName() + ":runMe");
    }

}

static class RunThem {

    static final Set<Runner> runners = new HashSet<>();

    static void runThem() {
        for (Runner r : runners) {
            r.runMe();
        }
    }
}

public void test() {
    Runner f = new FirstGood();
    Runner s = new SecondGood();
    RunThem.runThem();
}

Here all of your runMe objects extend a base class whose constructor installs the object in a Set held by the class that calls their runMe methods.

inline

void execute() throws Exception{
    for (Class<?> c : classesList)
    {
        //If you don't already have an instance then you need one 
        //note if the method is static no need for any existing instance.
        Object obj = Class.forName(c.getName());
        // name of the method and list of arguments to pass
        Method m = c.getDeclaredMethod(methodName,null);
        //method accessibility check
        if(!m.isAccessible()) 
            m.setAccessible(true);
        //invoke method if method with arguements then pass them as new Object[]{arg0...} instead of null  
        //if method is static then m.innvoke(null,null)
        m.invoke(obj, null);

    }
}

I would recommend using an Interface that defines the runMe() method and then have all your classes implement that interface. Then you would have a list of this Interface:

List<MyInterface> classes = new ArrayList<MyInterface>();

Then you could easily iterate over it and invoke "runMe()" on all of them or if you only want to invoke it for instances of a certain class you could do it like this:

public void execute(Class classForWhichToExecute) {
    for (MyInterface myInterface : classes) {
        if (classForWhichToExecute.isAssignableForm(myInterface)) {
            myInterface.runMe();
        }
    }
}

Of course this wouldn't work if your method is a static method - so adding more information from your side would help.

I would suggest to use an interface with a common method to override in each class. So that any class can be casted to interface and use its method to execute the method.

interface GoodAndBad{
    public void runMe();
}

Implemented class

class FirstGood implements GoodAndBad{
    @override
    public void runMe(){
       // Code to be executed
    }
}

You can use execute() method as follows

public void execute(List<GoodAndBad> classList){
    for(GoodAndBad c : classList){
        c.runMe();
     // Go inside that class, (maybe create an intance of that class) and  
       //  run the method called run me
    }
}

Change the Class to GoodAndBad interface to change the other method too.

This is loosely coupling objects to support favor over composition in Java Object Oriented Design Patterns.

Never use Strings of method names to execute a method at anytime. There are plenty of other cool solutions for that using design patterns.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM