Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to pass multiple parameters to a method in java reflections

Hi i am using reflections to achieve something. I have been given class name, method name of that class and parameter values that needs to be passed to that method in a file(Take any file. Not a constraint). I have to call that method with the parameters. This methods do not return anything. There is a huge list of methods in this classes and parameter list of each varies.

E.g: method1(String, String, int, boolean) method1(String, int, boolean) and likewise i have different permutations and combinations. So how can i achieve this. I have tried hard coding things with different switch clauses but it is a real overhead and risky thing to maintain. Can we dynamically do this thing, like on the fly read the method name and its parameter from the file and call it. Any small code snippet will be helpful. TIA.

like image 603
Mrunal Gosar Avatar asked Apr 25 '13 05:04

Mrunal Gosar


2 Answers

Hi all i have found the solution to the above question. below is the sample code snippet.

package reflections;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectionTest {
    public void method1(String str, int number) {
        System.out.println(str + number);
    }

    public void method1(String str) {
        System.out.println(str);
    }

    public void method1() {
        System.out.println("helloworld");
    }

    public static void main(String[] args) throws ClassNotFoundException,
            InstantiationException, IllegalAccessException,
            NoSuchMethodException, SecurityException, IllegalArgumentException,
            InvocationTargetException {
        // Step 1) Make an object array and store the parameters that you wish
        // to pass it.
        Object[] obj = {};// for method1()
        // Object[] obj={"hello"}; for method1(String str)
        // Object[] obj={"hello",1}; for method1(String str,int number)
        // Step 2) Create a class array which will hold the signature of the
        // method being called.
        Class<?> params[] = new Class[obj.length];
        for (int i = 0; i < obj.length; i++) {
            if (obj[i] instanceof Integer) {
                params[i] = Integer.TYPE;
            } else if (obj[i] instanceof String) {
                params[i] = String.class;
            }
            // you can do additional checks for other data types if you want.
        }

        String methoName = "method1"; // methodname to be invoked
        String className = "reflections.ReflectionTest";// Class name
        Class<?> cls = Class.forName(className);
        Object _instance = cls.newInstance();
        Method myMethod = cls.getDeclaredMethod(methoName, params);
        myMethod.invoke(_instance, obj);
    }
}

I hope this will help others too.

like image 143
Mrunal Gosar Avatar answered Nov 09 '22 20:11

Mrunal Gosar


public class ReflectionSample
{
    private Object mString = null;
    private int mValue;

    public ReflectionSample()
    {
    }

    public ReflectionSample(int oValue)
    {
        mValue = oValue;
    }

    public ReflectionSample(String oString)
    {
        mString = oString;
    }

    public ReflectionSample(String oString, int oValue)
    {
        setValues(oString, oValue);
    }

    public void setValues(String oString, int oValue)
    {
        mString = oString;
        mValue = oValue;
    }

    public String toString()
    {
        return ""+mString+":"+mValue;
    }

    public void run()
    {
        String oInput = "Teststring";
        Class<?> cls;
        String clsname = "main.ReflectionSample";
        Object rs = null;   // ReflectionSample
        Object rsc = null;

        System.out.println(this.getClass().getName());
        try
        {
            System.out.println(clsname);
            cls = Class.forName(clsname);
            if(cls == null)
            {
                System.err.println(clsname + " doesn't exist");
                return;
            }

            // Look for a constructor which has a single string
            Constructor<?> ct = null;
            Class<?>[] param_types = new Class<?>[1];
            Object[] arguments = new Object[1];

            param_types[0] = String.class;

            // get the string constructor
            ct = cls.getConstructor(param_types);

            // We only have one object
            arguments = new Object[1];
            arguments[0] = oInput;

            // Instantiate the object with passed in argument.
            rs = ct.newInstance(arguments);
            System.out.println("String constructor sample: "+rs);

            // Instantiate with default constructor
            param_types = new Class<?>[0];
            arguments = new Object[0];
            ct = cls.getConstructor(param_types);
            rs = ct.newInstance(arguments);
            rsc = rs; // Keep it for later, to lazy to call it again
            System.out.println("Default constructor sample: "+rs);

            // Instantiate with string and int constructor
            param_types = new Class<?>[2];
            arguments = new Object[2];

            // Must be in the same order as the params I think
            param_types[0] = String.class;
            param_types[1] = Integer.TYPE;      // <-- Its a primitive so use TYPE not Class

            arguments[0] = oInput;
            arguments[1] = new Integer(1);

            ct = cls.getConstructor(param_types);
            rs = ct.newInstance(arguments);
            System.out.println("String plus int constructor sample: "+rs);

            // call the setValues method
            param_types[0] = String.class;
            param_types[1] = Integer.TYPE;      // <-- Its a primitive so use TYPE not Class

            arguments[0] = oInput;
            arguments[1] = 1;

            System.out.println("setValues invocation before: "+rsc);
            Method m = cls.getMethod("setValues", param_types);
            m.invoke(rsc, arguments);
            System.out.println("setValues invocation after: "+rsc);

            // An alternative method to pass the parameters
            m = cls.getMethod("setValues", String.class, Integer.TYPE);
            m.invoke(rsc, oInput+"x", 2);
            System.out.println("setValues invocation after: "+rsc);
        }
        catch(Throwable e)
        {
            System.err.println(e.getLocalizedMessage());
        }
    }
}

Output:

main.ReflectionSample
main.ReflectionSample
String constructor sample: Teststring:0
Default constructor sample: null:0
String plus int constructor sample: Teststring:1
setValues invocation before: null:0
setValues invocation after: Teststring:1

Hope this helps.

I don't know if this is a newer feature in Java, but I have seen that you can use invoke now with parameters as well, instead of using an array, which might make your code better to read (This is the alternative way). If you need a variable number of arguments and you don't know beforehand how many there will be, allocating the array is defeinitly working and should also be backwardcompatible.

like image 37
Devolus Avatar answered Nov 09 '22 20:11

Devolus