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


683

如果我有两个变量:

Object obj;
String methodName = "getName";

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

被调用的方法没有参数,并且有String返回值。它是Java bean的吸气剂


3
使用反射API或使用Groovy
Peter Kelley,

Answers:


971

从臀部编码,将类似于:

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反射的信息


2
Java使用类型擦除的事实让我有点不高兴,但知道至少它具有反射功能,这再次让我振奋起来。现在唯一缺少的是对getter和setter的本地支持,或者C#中众所周知的属性。
7hi4g0 2014年

120
不公平-1。Henrik可能不提倡挤压异常,也没有为它们编写任何内容,因为他只是想证明自己的反思。
绘制

70
加一用于显示一些潜在的异常。如果我写了这个,那就是... catch(Exception e){...
mikbanUtah 2014年

1
我的“变量可能尚未初始化” methodmethod.invoke(obj, arg1, arg2,...);。一个method = null;可以解决问题,但在答案中提及它并不是一个坏主意。
阿敏

2
@ DeaMon1 Java方法不使用“退出代码”,但是如果该方法返回任何内容,invoke则将返回它返回的所有内容。如果运行该方法时发生异常,则该异常将包装在中InvocationTargetException
ThePyroEagle

194

从反射中使用方法调用

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[]并声明要传递给方法的参数

太酷了,我认为您对getDeclaredMethod()的使用是正确的,它可能比getMethod()更“安全”
。– braskkazoo

22
错误。是的,getDeclaredMethod确实适用于私有方法和受保护方法。但是:它不适用于超类中定义的方法(继承的方法)。因此,这在很大程度上取决于您要做什么。在许多情况下,无论定义该方法的确切类是什么,您都希望它能够工作。
jrudolph

我应该在哪里放置“类”文件?最好解释一下Eclipse IDE
Dr.jacky '16

@ Mr.Hyde在课程路径上。
Stijn de Witt

如果我正在调用的方法根本不接受任何参数,我应该放在method.invoke()里面吗?似乎我仍然必须提供第二个参数,应该是一些空的Object数组吗?
伊戈尔

100

对于那些想要Java 7中简单代码示例的人:

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, ...);

建议阅读: 创建新的类实例


1
最好的答案在这里。完整而简洁
Reuben JaMesAveñoGruta

1
正确的最佳答案。
Dhara Patel

你在哪里得到Method对象?
10/3

从反映pkg。

55

可以像这样调用该方法。还有更多可能性(请检查反射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);
    }



}

7
+1表示唯一确认OP在他的问题中指定“无参数”的答案(因为这也是我在寻找的内容)。
约翰·菲茨帕特里克

16

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

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


11
我不同意。编写这样的代码很安全是很容易的,而且我已经用多种语言做到了。例如,一个人可以创建一组允许的方法,并且仅允许在名称中包含该方法的情况下调用该方法。更加安全(但仍然简单)将每个允许的方法限制为特定状态,并且除非线程/接口/用户/任何符合此类条件的方法,否则不允许调用该方法。
JSON,

永远不要对此类问题这么挑剔。现在,我正在创建一个简单的程序,以允许用户使用Web界面在任意对象上定义任意任务。我知道这确实是不安全的,但是一旦收到配置,便会进行正确的测试,它允许非程序员轻松配置任务,还使程序能够将自定义类链接到通用代码(即为了使他们能够配置要通过Web界面使用的方法,我不必进行GUI修改,所以我使用了反射。
DGoiko

14

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

  • 静态或实例调用(在一种情况下,您不需要该类的实例,在另一种情况下,您可能需要依赖可能存在或可能不存在的现有默认构造函数
  • 公共或非公共方法调用(对于后者,您需要在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;
}

12
Object obj;

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

method.invoke(obj, null);

对象至少应具有一个或多个值。
洛瓦·奇图姆里

这对于我所需要的来说确实很好。我有一个已经实例化的类,只需要从中获取一个方法即可。在这里添加例外捕获是一个好主意,但是否则,这对我来说效果很好。我认为避免空异常的方法是使用可空值,但是我使用的方法名称范围非常有限(实际上只是1到4的计数器)。
in那教徒Waldrip

12
//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");

8

如果多次进行调用,则可以使用Java 7中引入的新方法句柄。在这里,我们为您的方法返回一个String:

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]

1
给未来的读者;如果您关心性能,则将invokeExact尽可能使用。为此,呼叫站点签名必须与方法句柄类型完全匹配。通常需要花些时间才能开始工作。在这种情况下,您需要使用以下methodHandle = methodHandle.asType(methodHandle.type().changeParameterType(0, Object.class));String result = (String) methodHandle.invokeExact(obj);
命令强制转换

7

这听起来像可以用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);
     }
  }
}

6

索引(更快)

您可以使用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(aNumber);
    }
}

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

1
这种技术似乎比反射好得多。
John O

真的好多了吗?
Dimitri Kopriwa

@DimitriKopriwa索引是您使用ram而不是CPU计算的方式。对于整数索引,算法的难度为O(1)
阿米尔·佛

5
Method method = someVariable.class.getMethod(SomeClass);
String status = (String) method.invoke(method);

SomeClass是类,someVariable是变量。


如果someVariable确实是一个对象,请调用someVariable.getClass()。同样,您不能使用类作为唯一参数来调用getMethod()。都不用方法调用方法。正确:someVariable.getClass()。getMethod(“ coolMethod”,parameterClasses).invoke(arguments);
Orangle

5

我这样做是这样的:

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

5

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

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

谢谢....


这不是在Java中比较字符串的方式。您必须使用.equals方法。否则,您只是在比较它们是相同的对象引用,而实际上并不关心对象引用-只是字符串内容是匹配项。您也可以通过反射按名称获取方法,因此不确定为什么要自己滚动吗?
螺谈

5

以下是准备使用的方法:

调用不带参数的方法:

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

使用以下方法,如下所示:

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

输出:

方法一  
方法B:  
	参数1-1  
	参数2-测试

3

学生.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();
        }
     }
}

1

您应该使用反射-初始化一个类对象,然后是该类中的一个方法,然后在具有可选参数的对象上调用此方法。请记住将以下代码段包装在try-catch块中

希望能帮助到你!

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

1

这对我来说很好:

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)来调用该方法。


1

使用 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);

0

使用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


-10

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

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

4
如果实例在编译时已经知道,为什么不这样做className.getName().toString()呢?您错过了整个反思的重点。
BalusC 2010年

就像我说的那样,在这种情况下是不必要的,但是假设您将始终知道实例是一种不良的编程习惯。
SMayne 2010年

2
@SMayne:我建议删除这篇文章。
lpapp 2014年

在这种情况下,糟糕的编程将是对您的补充
pdenti
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.