将方法名称作为字符串给出时,如何调用 Java 方法?

Posted

技术标签:

【中文标题】将方法名称作为字符串给出时,如何调用 Java 方法?【英文标题】:How do I invoke a Java method when given the method name as a string? 【发布时间】:2010-09-14 17:29:15 【问题描述】:

如果我有两个变量:

Object obj;
String methodName = "getName";

不知道obj的类,怎么调用methodName标识的方法呢?

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

【问题讨论】:

要么使用the reflection api,要么使用groovy 【参考方案1】:

在反射中使用method invocation:

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

地点:

"class name" 是类的名称 objectToInvokeOn 是 Object 类型,是您要在其上调用方法的对象 "method name" 是你要调用的方法的名称 parameterTypesClass[] 类型,并声明该方法采用的参数 paramsObject[] 类型,并声明要传递给方法的参数

【讨论】:

酷,我认为您使用 getDeclaredMethod() 是对的,它可能比 getMethod()“更安全”.. 错了。是的,getDeclaredMethod 确实适用于私有和受保护的方法。但是:它不适用于超类中定义的方法(继承方法)。所以,这很大程度上取决于你想做什么。在许多情况下,无论定义方法的确切类如何,您都希望它能够工作。 我应该把“类”文件放在哪里?最好解释一下 Eclipse IDE @Mr.Hyde 在类路径上。 如果我调用的方法根本不接受任何参数,我应该在其中放入什么和 method.invoke()?看来我还是要提供第二个参数,应该是一些空的Object数组吧?【参考方案2】:

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

【讨论】:

【参考方案3】:
Object obj;

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

method.invoke(obj, null);

【讨论】:

对象应该至少有一个/多个值。 这非常适合我的需要。我有一个已经实例化的类,只需要从中获取一个方法。在这里添加异常捕获是一个好主意,但除此之外,这对我来说非常有效。我认为我避免空异常的方法是使用可空值,但我使用的方法名称范围非常有限(实际上只是一个从 1 到 4 的计数器)。【参考方案4】:

从头开始编码,类似于:

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 Reflection

【讨论】:

对 Java 使用类型擦除这一事实感到有些不安,但知道至少它有反射让我再次振作起来:D 现在有了 Java 8 中的 lambdas,该语言真的跟上了速度随着现代的发展。现在唯一缺少的是对 getter 和 setter 或 C# 中已知的属性的原生支持。 不公平 -1。 Henrik 可能并不提倡压制异常,也没有为它们写任何东西,因为他只是想展示反思。 加一个用于显示一些潜在的异常。如果我写了这个,那将是 ... catch(Exception e) ... 我在method.invoke(obj, arg1, arg2,...); 中得到method 的“变量可能尚未初始化”。 method = null; 解决了问题,但在答案中提及它并不是一个坏主意。 @DeaMon1 Java 方法不使用“退出代码”,但如果该方法返回任何内容,invoke 将返回它返回的任何内容。如果运行该方法发生异常,异常将被包裹在InvocationTargetException中。【参考方案5】:

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




【讨论】:

+1 是唯一一个承认 OP 在他的问题中指定“无参数”的答案(因为这也是我正在寻找的)。【参考方案6】:

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

静态或实例调用(在一种情况下,您不需要类的实例,在另一种情况下,您可能需要依赖可能存在或不存在的现有默认构造函数 ) 公共或非公共方法调用(对于后者,您需要在 doPrivileged 块内的方法上调用 setAccessible,其他 findbugs won't be happy) 如果您想抛出大量 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;

【讨论】:

【参考方案7】:

首先,不要。避免这种代码。它往往是非常糟糕的代码并且也不安全(参见Secure Coding Guidelines for the Java Programming Language, version 2.0 的第 6 节)。

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

【讨论】:

我不同意。编写这样的代码很容易保证安全,而且我已经用多种语言编写过。例如,可以创建一组允许的方法,并且仅当方法的名称在集合中时才允许调用该方法。更安全(但仍然很简单)将每个允许的方法限制为特定状态,并且不允许调用该方法,除非线程/接口/用户/任何符合此类标准的东西。 永远不要对此类问题如此直截了当。现在我正在创建一个简单的程序,允许用户使用 Web 界面在任意对象上定义任意任务。我知道这确实是不安全的,但是一旦收到配置就会执行适当的测试,它允许非程序员轻松配置任务,并且还使程序能够将自定义类链接到通用代码(这就是我使用反射的部分,以允许他们通过 Web 界面配置要使用的方法),而无需更新 GUI。【参考方案8】:

对我来说,一个非常简单且万无一失的方法是简单地制作一个方法调用者方法,如下所示:

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

【讨论】:

如果实例在编译时已经知道,为什么不直接做className.getName().toString()?你错过了反思的全部意义。 就像我说的,在这种情况下是不必要的,但假设你总是知道实例是一个不好的编程习惯。 @SMayne:我建议删除这篇文章。 在这种情况下,糟糕的编程宁愿成为一种恭维【参考方案9】:
//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");

【讨论】:

【参考方案10】:

对于那些想要 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, ...);

推荐阅读:Creating New Class Instances

【讨论】:

这里的最佳答案。完整简洁 正确的最佳答案。 你从哪里得到Method对象? 来自反射包。【参考方案11】:

你应该使用反射——初始化一个类对象,然后是这个类中的一个方法,然后在一个带有可选参数的对象上调用这个方法。请记住将以下 sn-p 包装在 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);

【讨论】:

【参考方案12】:

这对我来说很好用:

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

【讨论】:

【参考方案13】:

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

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 方法。否则,您只是在比较它们是相同的对象引用,并且您实际上并不关心对象引用 - 只是字符串内容是匹配的。您还可以通过反射按名称获取方法,所以不知道为什么要自己滚动?【参考方案14】:
try 
    YourClass yourClass = new YourClass();
    Method method = YourClass.class.getMethod("yourMethodName", ParameterOfThisMethod.class);
    method.invoke(yourClass, parameter);
 catch (Exception e) 
    e.printStackTrace();

【讨论】:

【参考方案15】:

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

【讨论】:

【参考方案16】:
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);【参考方案17】:

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

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]

【讨论】:

致未来的读者;如果你关心性能,你会想尽可能使用invokeExact。为此,调用站点签名必须与方法句柄类型完全匹配。开始工作通常需要一些修修补补。在这种情况下,您需要使用 methodHandle = methodHandle.asType(methodHandle.type().changeParameterType(0, Object.class)); 强制转换第一个参数,然后像 String result = (String) methodHandle.invokeExact(obj); 一样调用 @JornVernee “只要有可能”到底是什么意思?【参考方案18】:

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

【讨论】:

【参考方案19】:

以下是即用型方法:

调用方法,不带参数:

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

输出:

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

【讨论】:

【参考方案20】:

索引(更快)

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

【讨论】:

这种技术似乎比反射好得多。 真的好多了吗? @DimitriKopriwa 索引是您使用 ram 而不是 CPU 计算的方式。对于整数索引,算法难度为O(1) 这应该是答案。非常干净的解决方案。我需要从 json 配置文件中读取方法名称。所以使用这种技术,我可能只使用 的 HashMap 而不是反射。【参考方案21】:

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

【讨论】:

【参考方案22】:

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

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
    


【讨论】:

【参考方案23】:

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

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

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

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

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

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

【讨论】:

以上是关于将方法名称作为字符串给出时,如何调用 Java 方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何在非托管 c++ dll 中查找调用方程序集名称

如何实现webservice的异步调用

java 当给定方法名称作为具有实际参数的String时,Java类调用对象/类上的方法。

如何使用 C# 中的反射调用将输入参数作为另一个类对象的方法?(方法给出参数异常)

Java 构造方法

java调用构建器如何将响应作为字符串进行记录?