当给定方法名称为字符串时,如何调用Java方法?

时间:2020-03-06 15:00:38  来源:igfitidea点击:

如果我有两个变量:

Object obj;
String methodName = "getName";

在不知道obj类的情况下,如何在其上调用由methodName标识的方法?

所调用的方法没有参数,并且有一个String返回值。它是Java Bean的吸气剂。

解决方案

从反射中使用方法调用:

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

在哪里:

  • ""类名""是类的名称
  • objectToInvokeOn的类型为Object,是我们要在其上调用方法的对象
  • ""方法名称""是我们要调用的方法的名称
  • 参数类型是类[]的类型,并声明该方法采用的参数
  • params是Object []类型,并声明要传递给方法的参数

这听起来像可以用Java Reflection包完成的事情。

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

特别是在"按名称调用方法"下:

导入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);
     }
  }
}

Object obj;

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

method.invoke(obj, null);

从臀部编码,将类似于:

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

参数标识我们需要的非常特定的方法(如果有多个重载可用,如果该方法没有参数,则仅给出methodName)。

然后我们通过调用该方法

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

同样,如果没有,请忽略.invoke中的参数。但是,是的。阅读有关Java反射的信息

可以像这样调用该方法。还有更多可能性(请检查反射API),但这是最简单的一种:

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);
    }

}

为了完成我同事的回答,我们可能需要密切注意:

  • 静态或者实例调用(在一种情况下,我们不需要该类的实例,在另一种情况下,我们可能需要依赖可能存在或者可能不存在的现有默认构造函数)
  • 公共或者非公共方法调用(对于后者,我们需要在doPrivileged块内的方法上调用setAccessible,否则其他findbug不会满意)
  • 如果要抛弃大量的Java系统异常,则将其封装到一个更易于管理的应用异常中(因此,下面的代码中的CCException)

这是一个旧的Java1.4代码,其中考虑了这些要点:

/**
 * 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;
}

首先,不要。避免这种代码。它往往是很糟糕的代码,而且也很不安全(请参阅《安全编码指南》的第6节
Java编程语言2.0版)。

如果必须这样做,则最好使用java.beans进行反射。豆包裹反射,允许相对安全和常规的访问。

对我来说,一种非常简单且可靠的方法是简单地使方法调用方方法像这样:

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

然后,当我们需要调用该方法时,只需输入如下内容

//calling a toString method is unnessary here, but i use it to have my programs to both rigid and self-explanitory 
System.out.println(methodCaller(methodName).toString());