【问题标题】:Dynamically calling a class method in java?在java中动态调用类方法?
【发布时间】:2023-03-16 07:15:01
【问题描述】:

是否可以从 java 中动态调用类的方法?

例如,假设我有一个类的引用,例如字符串:'com.foo.Bar',或com.foo.Bar.class,或任何其他需要的东西..)。我有一个字符串数组/列表,例如[First, Last, Email]

我想简单地遍历这个数组,并在我引用的类上调用方法'validate' + element。例如:

MyInterface item = //instantiate the com.foo.Bar class here somehow, I'm not sure how.

item.validateFirst();
item.validateLast();
item.validateEmail();

我希望上述代码行动态发生,因此我可以更改对不同类的引用,并且我的字符串列表中的名称可以更改,但它仍然会调用它所拥有的任何类的 validate + name 方法参考。

这可能吗?

【问题讨论】:

标签: java


【解决方案1】:

最简单的方法是使用reflection

鉴于...

package com.foo;

public class Bar {

    public void validateFirst() {
        System.out.println("validateFirst");
    }

    public void validateLast() {
        System.out.println("validateLast");
    }

    public void validateEmail() {
        System.out.println("validateEmail");
    }

}

你可以使用类似...

String methodNames[] = new String[]{"First", "Last", "Email"};
String className = "com.foo.Bar";
try {

    Class classRef = Class.forName(className);
    Object instance = classRef.newInstance();

    for (String methodName : methodNames) {

        try {

            Method method = classRef.getDeclaredMethod("validate" + methodName);
            method.invoke(instance);

        } catch (NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) {
            ex.printStackTrace();
        }

    }

} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
    ex.printStackTrace();
}

查找方法并执行它们。

您需要决定处理错误的最佳方式以及它们对您的意义,但将想法扩展为可重用的方法并不难...

更新了 cmets 中讨论的概念概念

鉴于....

public interface Validator {
    public boolean isValid(Properties formProperties);
}

我们可以创建一个或多个...

public class UserRegistrationValidator implements Validator {
    public boolean isValid(Properties formProperties) {
        boolean isValid = false;
        // Required fields...
        if (formProperties.containsKey("firstName") && formProperties.containsKey("lastName") && formProperties.containsKey("email")) {
            // Further processing, valid each required field...
        }
        if (isValid) {
            // Process optional parameters
        }
        return isValid;
    }
}

然后从我们的输入控制器中,我们可以查看并验证所需的表单

public class FormController ... {

    private Map<String, Validator> validators;

    public void validForm(String formName, Properties formProperties) {
        boolean isValid = false;
        Validator validator = validators.get(formName);
        if (validator != null) {
            isValid = validate.isValid(formProperties);
        }
        return isValid;
    }

}

当然你需要提供一些方法来注册Validators,并且根据你使用的主干框架和你可以使用的参数可能会有差异(你不必使用Properties,但它基本上只是一个Map&lt;String, String&gt;...)

【讨论】:

  • 我需要在运行时和正常情况下发生这种情况。具体来说,这将是在 Web 应用程序中进行表单验证。你还会推荐反射吗?
  • 或者我可以构建一个单独的Validator 类,它在模型上调用getRequiredFields() 方法以获取字段名称列表,然后循环它们以调用相应的validate 方法。你有什么建议?
  • Validator 实现可以根据需要扩展到各个方法,这取决于您。因为您无法定义一个interface,它可能满足所有需求或有任何有用的方式来调用它实现(您需要一个单独的if 为每个可能的形式知道Validator 的哪个实例使用),我会说......不......
  • Reflection 确实会受到性能影响,此外,还可以调用它所反射的对象的 privateprotected 方法......这实际上相当简洁,但可能会破坏部分如果不明智地使用代码...不要误会我的意思。我经常使用反射,我只是花时间确定它是否是问题的最佳解决方案。仅仅因为它有效,并不意味着它是对的(或错的);)
  • 我不能说大多数网络框架或它们在哪里。例如,我想他们可能正在做对象到数据库的映射。至于性能,看看this,你也应该看看offical tutorial
【解决方案2】:

你可以写这样的东西......它将类的名称作为字符串作为参数,方法名称及其参数

private static String invoke(String aClass, String aMethod, Class<?>[] params,
            Object[] args) throws Exception {
        String resp = "";
        Class<?> c = Class.forName(aClass);
        Method m = c.getDeclaredMethod(aMethod, params);
        Object i = c.newInstance();
        resp = m.invoke(i, args).toString();

        return resp;
}

你也可以参考oracle的反射教程...演示了如何调用方法 http://docs.oracle.com/javase/tutorial/reflect/member/methodInvocation.html

【讨论】:

    【解决方案3】:

    可以使用reflection

    1. 首先,从 FQN(完全限定名,即包含包的类名)创建一个新类。

    2. 然后您遍历您的 elements 并在您的项目上调用 "validate" 方法。

        Class<?> clazz = Class.forName("com.foo.Bar");
        Object item = clazz.newInstance(); 
        for (String element : elements) {
            Method method = clazz.getDeclaredMethod("validate" + element);
            method.invoke(item);
        }
    

    【讨论】:

    • 太棒了..在生产过程中这样做有什么缺点吗?
    • Reflection 有一些轻微的性能劣势,也使代码的可维护性降低(考虑重构工具)。但是,我能想到的所有 Web 框架都或多或少地使用了反射,所以考虑到它的缺点,你也可以在生产中使用它。
    【解决方案4】:

    可以使用反射,但我最喜欢的方法是使用beanutils,例如:

    Bar b1 = //...
    BeanUtils.getProperty(b1, "first");
    BeanUtils.getProperty(b1, "last");
    

    请注意,您的类必须符合 javabean 约定。您可以阅读有关 beanutils 的更多信息on this blog post(免责声明我是博客作者)

    【讨论】:

    • 这只是给出了 getter 的值。你需要MethodUtils.invokeMethod
    • 为什么要使用一个全新的 jar 来调用动态 3 种方法?
    • @SilviuBurcea 因为你不想“重新发明轮子”
    • gerrytan,我不觉得我在重新发明轮子。如果我经常使用你的借口,我最终会得到 1 个类应用程序和 1 GB 的 jar 依赖项。对于一次性 3 方法调用,它不值得。 @ClickUpvote 是的,BeanUtils 是反射 API 的包装器(根据他们的文档)
    【解决方案5】:

    如果您事先知道类的名称,请使用Class.forName(yourClassname) 这样,您可以调用该类,然后您可以调用它的方法。

    【讨论】:

      【解决方案6】:

      是的,使用反射。 在你的对象上使用Class.getDeclaredMethod

      Object validator = <your object instance>;
      final String[] values = {
        "Item1","Item2","Item3"
      }
      for(final String s : values) {
        Method m = validator.getDeclaredMethod("validate" + s,String.class);
        try {
          Object result = m.invoke(validator, s);
        }
        catch(ex) {}
      }
      

      【讨论】:

        猜你喜欢
        • 2014-08-17
        • 1970-01-01
        • 2021-10-22
        • 1970-01-01
        • 1970-01-01
        • 2012-07-20
        • 2012-10-28
        • 2010-09-20
        • 2010-09-21
        相关资源
        最近更新 更多