views:

24531

answers:

10

If I have two variables:

Object obj;
String methodName = "getName";

Without knowing the class of obj, how can I call the method identified by methodName on it?
The method being called has no parameters, and a String return value - a getter for a Java bean.

+10  A: 

Use reflection:

http://java.sun.com/docs/books/tutorial/reflect/member/methodInvocation.html

Class<?> c = Class.forName("class name");
Method  method = c.getDeclaredMethod ("method name", parameterTypes)
method.invoke (objectToInvokeOn, params)

Where:

"class name" is the name of the class

objectToInvokeOn is of type Object and is the object you want to invoke the method on "method name" is the name of the method you want to call

parameterTypes is of type Class [] and decalres the parameters the method takes

params is of type Object [] and declares the parameters to be passed to the method

Owen
Cool, I think you're right with getDeclaredMethod(), it is probably 'safer' than getMethod()..
brass-kazoo
Wrong. Yes, getDeclaredMethod does work with private and protected methods. BUT: it does not work with methods defined in superclasses (inherited methods). So, it depends strongly on what you want to do. In many cases you want it to work regardless of the exact class in which the method is defined.
jrudolph
Thanks for the explanation!
brass-kazoo
A: 

Either use the reflection api or use groovy

Peter Kelley
A: 

This sounds like something that is doable with the Java Reflection package.

http://java.sun.com/developer/technicalArticles/ALT/Reflection/index.html

Particularly under Invoking Methods by Name:

import java.lang.reflect.*;

public class method2 {
  public int add(int a, int b)
  {
     return a + b;
  }

  public static void main(String args[])
  {
     try {
       Class cls = Class.forName("method2");
       Class partypes[] = new Class[2];
        partypes[0] = Integer.TYPE;
        partypes[1] = Integer.TYPE;
        Method meth = cls.getMethod(
          "add", partypes);
        method2 methobj = new method2();
        Object arglist[] = new Object[2];
        arglist[0] = new Integer(37);
        arglist[1] = new Integer(47);
        Object retobj 
          = meth.invoke(methobj, arglist);
        Integer retval = (Integer)retobj;
        System.out.println(retval.intValue());
     }
     catch (Throwable e) {
        System.err.println(e);
     }
  }
}
zxcv
+1  A: 
Object obj;

Method method = obj.getClass().getMethod("methodName", null);

method.invoke(obj, null);
Ash Kim
+17  A: 

Coding from the hip, it would be something like:

Method method;
try {
  method = obj.getClass().getMethod(methodName, param1.class, param2.class, ..);
} catch (SecurityException e) {
  // ...
} catch (NoSuchMethodException e) {
  // ...
}

The parameters identify the very specific method you need (if there are several overloaded available, if the method has no arguments, only give methodName).

Then you invoke that method by calling

try {
  method.invoke(obj, arg1, arg2,...);
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {

Again, leave out the arguments in .invoke, if you don't have any. But yeah. Read about Java Reflection

Henrik Paul
+3  A: 

The method can be invoked like this. There are also more possibilities (check the reflection api), but this is the simplest one:

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

import org.junit.Assert;
import org.junit.Test;

public class ReflectionTest {

    private String methodName = "length";
    private String valueObject = "Some object";

    @Test
    public void testGetMethod() throws SecurityException, NoSuchMethodException, IllegalArgumentException,
            IllegalAccessException, InvocationTargetException {
        Method m = valueObject.getClass().getMethod(methodName, new Class[] {});
        Object ret = m.invoke(valueObject, new Object[] {});
        Assert.assertEquals(11, ret);
    }



}
Petr Macek
+2  A: 

To complete my colleague's answers, You might want to pay close attention to:

  • static or instance calls (in one case, you do not need an instance of the class, in the other, you might need to rely on an existing default constructor that may or may not be there)
  • public or non-public method call (for the latter,you need to call setAccessible on the method within an doPrivileged block, other findbugs won't be happy)
  • encapsulating into one more manageable applicative exception if you want to throw back the numerous java system exceptions (hence the CCException in the code below)

Here is an old java1.4 code which takes into account those points:

/**
 * Allow for instance call, avoiding certain class circular dependencies. <br />
 * Calls even private method if java Security allows it.
 * @param aninstance instance on which method is invoked (if null, static call)
 * @param classname name of the class containing the method 
 * (can be null - ignored, actually - if instance if provided, must be provided if static call)
 * @param amethodname name of the method to invoke
 * @param parameterTypes array of Classes
 * @param parameters array of Object
 * @return resulting Object
 * @throws CCException if any problem
 */
public static Object reflectionCall(final Object aninstance, final String classname, final String amethodname, final Class[] parameterTypes, final Object[] parameters) throws CCException
{
    Object res;// = null;
    try {
     Class aclass;// = null;
     if(aninstance == null)
     {
      aclass = Class.forName(classname);
     }
     else
     {
      aclass = aninstance.getClass();
     }
     //Class[] parameterTypes = new Class[]{String[].class};
    final Method amethod = aclass.getDeclaredMethod(amethodname, parameterTypes);
     AccessController.doPrivileged(new PrivilegedAction() {
 public Object run() {
                amethod.setAccessible(true);
                return null; // nothing to return
      }
     });
     res = amethod.invoke(aninstance, parameters);
    } catch (final ClassNotFoundException e) {
     throw new CCException.Error(PROBLEM_TO_ACCESS+classname+CLASS, e);
    } catch (final SecurityException e) {
     throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_SECURITY_ISSUE, e);
    } catch (final NoSuchMethodException e) {
     throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_NOT_FOUND, e);
    } catch (final IllegalArgumentException e) {
     throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_ILLEGAL_ARGUMENTS+String.valueOf(parameters)+GenericConstants.CLOSING_ROUND_BRACKET, e);
    } catch (final IllegalAccessException e) {
     throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_ACCESS_RESTRICTION, e);
    } catch (final InvocationTargetException e) {
 throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_INVOCATION_ISSUE, e);
    } 
    return res;
}
VonC
+4  A: 

First, don't. Avoid this sort of code. It tends to be really bad code and insecure too (see section 6 of Secure Coding Guidelines for the Java Programming Language, version 2.0).

If you must do it, prefer java.beans to reflection. Beans wraps reflection allowing relatively safe and conventional access.

Tom Hawtin - tackline
+1  A: 

There is an Eclipse Plugin that you can use to generate private method invocation. So you don't need to code formal arguments and actual arguments every time you need to invoke a private method (an usual scenario in unit testing).

http://code.google.com/p/hexacta-booster/

A: 

for me a pretty simple and fool proof way would be to simply make a method caller method like so:

public static object methodCaller(String methodName)
{
if(methodName.equals("getName"))
     return className.getName();
}

then when you need to call the method simply put something like this

System.out.println(methodCaller(methodName).toString()); 

//calling a toString method is unnessary here, but i use it to have my programs to both rigid and self-explanitory

SMayne
If the instance is already known during compiletime, why don't you just do `className.getName().toString()`? You're missing the whole point of reflection.
BalusC
Like I said, unnecessary in this case, but assuming you'll always know the instance is a bad programming habit.
SMayne