如何调用一个Java方法时,给定的方法名称作为一个字符串?

如果我有两个变量:

Object obj;
String methodName = "getName";

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

被调用的方法没有参数,只有一个String返回值。它是Java的吸气剂

1033183 次浏览

从反射中使用方法调用

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

在哪里:

  • "class name"是类的名称
  • objectToInvokeOn是Object类型,是要在其上调用方法的对象
  • "method name"是您要调用的方法的名称
  • parameterTypes的类型为Class[],并声明方法采用的参数
  • params类型为Object[]并声明要传递给方法的参数

这听起来像是Java反射包可以做到的事情。

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块中的方法上调用setAccesable,其他Findbugs不会高兴的
  • 如果您想抛回众多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反射。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());
//Step1 - Using string funClass to convert to class
String funClass = "package.myclass";
Class c = Class.forName(funClass);


//Step2 - instantiate an object of the class abov
Object o = c.newInstance();
//Prepare array of the arguments that your function accepts, lets say only one string here
Class[] paramTypes = new Class[1];
paramTypes[0]=String.class;
String methodName = "mymethod";
//Instantiate an object of type method that returns you method name
Method m = c.getDeclaredMethod(methodName, paramTypes);
//invoke method with actual params
m.invoke(o, "testparam");

对于那些想要在Java7中直接代码示例的人:

Dog类:

package com.mypackage.bean;


public class Dog {
private String name;
private int age;


public Dog() {
// empty constructor
}


public Dog(String name, int age) {
this.name = name;
this.age = age;
}


public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}


public int getAge() {
return age;
}


public void setAge(int age) {
this.age = age;
}


public void printDog(String name, int age) {
System.out.println(name + " is " + age + " year(s) old.");
}
}

ReflectionDemo类:

package com.mypackage.demo;


import java.lang.reflect.*;


public class ReflectionDemo {


public static void main(String[] args) throws Exception {
String dogClassName = "com.mypackage.bean.Dog";
Class<?> dogClass = Class.forName(dogClassName); // convert string classname to class
Object dog = dogClass.newInstance(); // invoke empty constructor


String methodName = "";


// with single parameter, return void
methodName = "setName";
Method setNameMethod = dog.getClass().getMethod(methodName, String.class);
setNameMethod.invoke(dog, "Mishka"); // pass arg


// without parameters, return string
methodName = "getName";
Method getNameMethod = dog.getClass().getMethod(methodName);
String name = (String) getNameMethod.invoke(dog); // explicit cast


// with multiple parameters
methodName = "printDog";
Class<?>[] paramTypes = {String.class, int.class};
Method printDogMethod = dog.getClass().getMethod(methodName, paramTypes);
printDogMethod.invoke(dog, name, 3); // pass args
}
}

输出: Mishka is 3 year(s) old.


您可以通过以下方式使用参数调用构造函数:

Constructor<?> dogConstructor = dogClass.getConstructor(String.class, int.class);
Object dog = dogConstructor.newInstance("Hachiko", 10);

或者,您可以删除

String dogClassName = "com.mypackage.bean.Dog";
Class<?> dogClass = Class.forName(dogClassName);
Object dog = dogClass.newInstance();

并做

Dog dog = new Dog();


Method method = Dog.class.getMethod(methodName, ...);
method.invoke(dog, ...);

推荐阅读:创建新的类实例

您应该使用反射-init一个类对象,然后是这个类中的一个方法,然后在带有可选参数的对象上调用此方法。请记住将以下片段包装在尝试捕获块中

希望有帮助!

Class<?> aClass = Class.forName(FULLY_QUALIFIED_CLASS_NAME);
Method method = aClass.getMethod(methodName, YOUR_PARAM_1.class, YOUR_PARAM_2.class);
method.invoke(OBJECT_TO_RUN_METHOD_ON, YOUR_PARAM_1, YOUR_PARAM_2);

这对我来说工作得很好:

public class MethodInvokerClass {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, ClassNotFoundException, InvocationTargetException, InstantiationException {
Class c = Class.forName(MethodInvokerClass.class.getName());
Object o = c.newInstance();
Class[] paramTypes = new Class[1];
paramTypes[0]=String.class;
String methodName = "countWord";
Method m = c.getDeclaredMethod(methodName, paramTypes);
m.invoke(o, "testparam");
}
public void countWord(String input){
System.out.println("My input "+input);
}

}

输出:

My input testparam

我可以通过将其名称传递给另一个方法(如main)来调用该方法。

请参考以下代码可以帮助您。

public static Method method[];
public static MethodClass obj;
public static String testMethod="A";


public static void main(String args[])
{
obj=new MethodClass();
method=obj.getClass().getMethods();
try
{
for(int i=0;i<method.length;i++)
{
String name=method[i].getName();
if(name==testMethod)
{
method[i].invoke(name,"Test Parameters of A");
}
}
}
catch(Exception ex)
{
System.out.println(ex.getMessage());
}
}

谢谢……

try {
YourClass yourClass = new YourClass();
Method method = YourClass.class.getMethod("yourMethodName", ParameterOfThisMethod.class);
method.invoke(yourClass, parameter);
} catch (Exception e) {
e.printStackTrace();
}

使用import java.lang.reflect.*;

public static Object launchProcess(String className, String methodName, Class<?>[] argsTypes, Object[] methodArgs)
throws Exception {


Class<?> processClass = Class.forName(className); // convert string classname to class
Object process = processClass.newInstance(); // invoke empty constructor


Method aMethod = process.getClass().getMethod(methodName,argsTypes);
Object res = aMethod.invoke(process, methodArgs); // pass arg
return(res);
}

这是你如何使用它:

String className = "com.example.helloworld";
String methodName = "print";
Class<?>[] argsTypes = {String.class,  String.class};
Object[] methArgs = { "hello", "world" };
launchProcess(className, methodName, argsTypes, methArgs);
Method method = someVariable.class.getMethod(SomeClass);
String status = (String) method.invoke(method);

SomeClass是类,someVariable是变量。

如果您多次调用,您可以使用Java7中引入的新方法句柄。下面我们来看看您的方法返回一个字符串:

Object obj = new Point( 100, 200 );
String methodName = "toString";
Class<String> resultType = String.class;


MethodType mt = MethodType.methodType( resultType );
MethodHandle methodHandle = MethodHandles.lookup().findVirtual( obj.getClass(), methodName, mt );
String result = resultType.cast( methodHandle.invoke( obj ) );


System.out.println( result );  // java.awt.Point[x=100,y=200]

Student.java

class Student{
int rollno;
String name;


void m1(int x,int y){
System.out.println("add is" +(x+y));
}


private void m3(String name){
this.name=name;
System.out.println("danger yappa:"+name);
}
void m4(){
System.out.println("This is m4");
}
}

StudentTest.java

import java.lang.reflect.Method;
public class StudentTest{


public static void main(String[] args){


try{


Class cls=Student.class;


Student s=(Student)cls.newInstance();




String x="kichha";
Method mm3=cls.getDeclaredMethod("m3",String.class);
mm3.setAccessible(true);
mm3.invoke(s,x);


Method mm1=cls.getDeclaredMethod("m1",int.class,int.class);
mm1.invoke(s,10,20);


}
catch(Exception e){
e.printStackTrace();
}
}
}

以下是准备使用的方法:

调用一个方法,不带参数:

public static void callMethodByName(Object object, String methodName) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
object.getClass().getDeclaredMethod(methodName).invoke(object);
}

要使用Arguments调用方法:

    public static void callMethodByName(Object object, String methodName, int i, String s) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
object.getClass().getDeclaredMethod(methodName, int.class, String.class).invoke(object, i, s);
}

使用上述方法如下:

package practice;


import java.io.IOException;
import java.lang.reflect.InvocationTargetException;


public class MethodInvoke {


public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException {
String methodName1 = "methodA";
String methodName2 = "methodB";
MethodInvoke object = new MethodInvoke();
callMethodByName(object, methodName1);
callMethodByName(object, methodName2, 1, "Test");
}


public static void callMethodByName(Object object, String methodName) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
object.getClass().getDeclaredMethod(methodName).invoke(object);
}


public static void callMethodByName(Object object, String methodName, int i, String s) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
object.getClass().getDeclaredMethod(methodName, int.class, String.class).invoke(object, i, s);
}


void methodA() {
System.out.println("Method A");
}


void methodB(int i, String s) {
System.out.println("Method B: "+"\n\tParam1 - "+i+"\n\tParam 2 - "+s);
}
}

输出:

Method A
Method B:
	Param1 - 1
	Param 2 - Test

索引(更快)

您可以使用FunctionalInterface将方法保存在容器中以对它们进行索引。您可以使用数组容器通过数字调用它们,或者使用哈希映射通过字符串调用它们。通过这个技巧,您可以索引您的方法以动态调用它们更快

@FunctionalInterface
public interface Method {
double execute(int number);
}


public class ShapeArea {
private final static double PI = 3.14;


private Method[] methods = {
this::square,
this::circle
};


private double square(int number) {
return number * number;
}


private double circle(int number) {
return PI * number * number;
}


public double run(int methodIndex, int number) {
return methods[methodIndex].execute(number);
}
}

lambda语法

你也可以使用lambda语法:

public class ShapeArea {
private final static double PI = 3.14;


private Method[] methods = {
number -> {
return number * number;
},
number -> {
return PI * number * number;
},
};


public double run(int methodIndex, int number) {
return methods[methodIndex].execute(number);
}
}

2022年编辑

刚才我在考虑为您提供一个通用的解决方案,以处理具有可变数量参数的所有可能方法:

@FunctionalInterface
public interface Method {
Object execute(Object ...args);
}


public class Methods {
private Method[] methods = {
this::square,
this::rectangle
};


private double square(int number) {
return number * number;
}


private double rectangle(int width, int height) {
return width * height;
}


public Method run(int methodIndex) {
return methods[methodIndex];
}
}

用法

methods.run(1).execute(width, height);

对于JooR,它仅仅是:

on(obj).call(methodName /*params*/).get()

下面是一个更详细的例子:

public class TestClass {


public int add(int a, int b) { return a + b; }
private int mul(int a, int b) { return a * b; }
static int sub(int a, int b) { return a - b; }


}


import static org.joor.Reflect.*;


public class JoorTest {


public static void main(String[] args) {
int add = on(new TestClass()).call("add", 1, 2).get(); // public
int mul = on(new TestClass()).call("mul", 3, 4).get(); // private
int sub = on(TestClass.class).call("sub", 6, 5).get(); // static
System.out.println(add + ", " + mul + ", " + sub);
}
}

此打印:

3、12、1

对于那些从非静态方法调用同一类中的方法的人,请参阅下面的代码:

class Person {
public void method1() {
try {
Method m2 = this.getClass().getDeclaredMethod("method2");
m1.invoke(this);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}


public void method2() {
// Do something
}


}

假设您正在从同一个类中的静态方法调用静态方法。为此,您可以对以下代码进行示例。

class MainClass
{
public static int foo()
{
return 123;
}


public static void main(String[] args)
{
Method method = MainClass.class.getMethod("foo");
int result = (int) method.invoke(null); // answer evaluates to 123
}
}

为了解释,因为我们不希望在这里执行真正的面向对象编程,从而避免创建不必要的对象,我们将利用class属性来调用getMethod()

然后我们将为invoke()方法传入null,因为我们没有对象来执行此操作。

最后,因为我们,程序员,知道我们期待一个整数,那么 我们显式地将invoke()调用的返回值转换为整数。

现在您可能想知道:“在Java中进行所有这些非面向对象程序设计有什么意义?”

我的用例是在Java解决Project Euler问题。我有一个包含所有解决方案的Java源文件,我想传入命令行参数来确定要运行哪个Project Euler问题。