3

I am trying to make a program that executes a particular method when the class name and method name are passed as String parameter to the caller. Consider the code below. I have a class CarBean :

public class CarBean {

    private String brand;
    private String color;

    /**
     * @return the brand
     */
    public String getBrand() {
        return brand;
    }

    /**
     * @param the brand to set
     */
    public void setBrand(String brand) {
        this.brand= brand;
    }

    /**
     * @return the color
     */
    public String getColor() {
        return color;
    }

    /**
     * @param the color to set
     */
    public void setColor(String color) {
        this.color= color;
    }

}

Now I want to run this via a method as below :

runTheMehod("CarBean","getColor");

The implementation of runTheMethod would be like this :

public runTheMethod(String className, String methodName){
try {
            Object carObj = Class.forName(className).newInstance();
            //What to do now???
        } catch (InstantiationException | IllegalAccessException
                | ClassNotFoundException e) {
            e.printStackTrace();
        }
}

I can get an object using the class name. Now I need to cast it to a CarBean object and then I can run its method. So wondering how to cast it at runtime as the classname would be different for each call. Also, can I check whether the class has specific method before I try call it?

Any suggestion on the problem would be appreciated. Also, I'm all ears to know if there is a better approach to do this.

Crickcoder
  • 2,135
  • 4
  • 22
  • 36
  • 2
    https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getMethod%28java.lang.String,%20java.lang.Class...%29, https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Method.html#invoke%28java.lang.Object,%20java.lang.Object...%29 – JB Nizet Nov 13 '14 at 22:33
  • 1
    Are you sure you want to instantiate a fresh object of that type? e.g. `runTheMethod(Object instance, String methodName)` would work too and leave it up to you to create objects of that type. – zapl Nov 13 '14 at 22:34
  • @zapl Yes I am getting the class name in string format – Crickcoder Nov 13 '14 at 22:35
  • It is doable with Java Reflection, but you should think about a better design before trying that solution. – Dici Nov 13 '14 at 22:36
  • @Dici I am open to any suggestion that can improve the design – Crickcoder Nov 13 '14 at 22:40
  • @GV with your example I don't see how using reflection can be simpler than not using it. I cannot propose another design since I don't know what you want to do. – Dici Nov 13 '14 at 22:42
  • @GV Check out [Apache Commons BeanUtils](http://commons.apache.org/proper/commons-beanutils/). – Jason C Nov 13 '14 at 22:43
  • so basically I'll be reading className and methodname from and excel file or database. So it'll be in string format and not object. Then I will be running them one by one. – Crickcoder Nov 13 '14 at 22:44
  • Since you're always creating a new object it will never have a color, or brand, or anything else, so you could simply replace all of your code with `return null`. This has the advantage of being significantly faster than using reflection. – David Conrad Nov 13 '14 at 23:19
  • 1
    @DavidConrad I understand that, but I'll be using it for other methods at Action/Service/DAO layers which will return values according to inputs provided – Crickcoder Nov 13 '14 at 23:21
  • Possible duplicate of [How to pass method name dynamically in java](https://stackoverflow.com/questions/22419511/how-to-pass-method-name-dynamically-in-java) – Nevin Jul 31 '19 at 00:25

3 Answers3

4

What to do now???

You can now call the method on your carObj like this:

Method method = carObj.getClass().getMethod(methodName);
method.invoke(carObj);

See also:

Method.invoke

peter.petrov
  • 38,363
  • 16
  • 94
  • 159
  • Thanks for your inputs. This works well. I have one more question: If I want to extend the implementation, I pass the return value also as input and compare it, then I'll either need to cast it or implement the hashCode for CarBean (or every class I pass). Any suggestion on that ? - public runTheMethod(String className, String methodName, Object returnValue) – Crickcoder Nov 13 '14 at 23:01
  • Yes, you need to do some casts then or ... just compare them (the `returnValue` passed in and the actual returned value) as `Object`s by using their `equals` method (but be careful with `null` in case one or both of them are `null`). – peter.petrov Nov 13 '14 at 23:02
1

If all you are trying to do is call a method by name, you don't need to cast it to anything. It is sufficient to use the Object with further reflection.

See Class.getDeclaredMethod(), as in:

Object carObj = ...;
Method method = carObj.getClass().getDeclaredMethod(methodName, ...);
Object retObj = method.invoke(carObj, ...);

Note that we don't care what type carObj actually is, although if you wanted to check you could always use instanceof or Class.isAssignableFrom or Class.isInstance.

It is a bit weird, though, that you are instantiating a new object then calling one of its methods all in one go. That object goes away once your runTheMethod returns.


By the way, it looks like you're just trying to get and set bean properties. You might want to have a look at Apache Commons BeanUtils instead, then your example becomes simply:

CarBean bean = ...;
String color = (String)PropertyUtils.getSimpleProperty(bean, "color"); // calls getter.
Jason C
  • 38,729
  • 14
  • 126
  • 182
  • Thanks for your inputs. I used CarBean as an example but it's not limited to a Java bean. I might have to run this for other methods as in Action/Service/DAO layers. – Crickcoder Nov 13 '14 at 22:49
  • 1
    Careful with `getDeclaredMethod` vs `getMethod`, first just returns methods (re)defined in this the class - inherited methods are not found, latter just public methods: http://ideone.com/veKNyU – zapl Nov 13 '14 at 22:51
1

I doubt you really need to do this. I suspect you have an XY Problem. Your question is somewhat similar to asking whether Java has an eval function and the correct answer is similar:

First ask yourself, where did these String come from? Did another part of your program generate them, or was it input provided by the user?

  • Another part of my program generated it: so, you want one part of your program to decide the kind of operation to perform, but not perform the operation, and a second part that performs the chosen operation. Instead of generating and then evaluating Strings, use the Strategy, Command or Builder design pattern, as appropriate for your particular case.

  • It is user input: the user could input anything, including class and method names that, when executed, could cause your program to misbehave, crash, expose information that should be secret, damage persistent information (such as the content of a database), and other such nastiness. The only way to prevent that would be to parse the Strings and then either immediately execute the method on a successful parse, or have the parser create a Command object for later execution.

Raedwald
  • 46,613
  • 43
  • 151
  • 237