通过将类名和方法名作为参数传递来运行Java方法

3

我正在尝试编写一个程序,当类名和方法名以字符串参数的形式传递给调用者时,执行特定的方法。请考虑下面的代码。我有一个名为CarBean的类:

public class CarBean {

    private String brand;
    private String color;

    /**
     * @return the brand
     */
    public String getBrand() {
        return brand;
    }

    /**
     * @param the brand to set
     */
    public void setBrand(String brand) {
        this.brand= brand;
    }

    /**
     * @return the color
     */
    public String getColor() {
        return color;
    }

    /**
     * @param the color to set
     */
    public void setColor(String color) {
        this.color= color;
    }

}

现在我想通过以下方法运行它:
runTheMehod("CarBean","getColor");

runTheMethod的实现应该像这样:
public runTheMethod(String className, String methodName){
try {
            Object carObj = Class.forName(className).newInstance();
            //What to do now???
        } catch (InstantiationException | IllegalAccessException
                | ClassNotFoundException e) {
            e.printStackTrace();
        }
}

我可以使用类名获得一个对象。现在我需要将其转换为CarBean对象,然后才能运行它的方法。所以想知道如何在运行时进行强制类型转换,因为每次调用的类名都不同。另外,我能否在尝试调用该方法之前检查类是否具有特定方法?
如有任何建议,请告知。如果有更好的方法解决此问题,我也愿意听取。

2
https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getMethod%28java.lang.String,%20java.lang.Class...%29获取具有给定名称和参数的公共成员方法类对象。返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定公共成员方法。https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Method.html#invoke%28java.lang.Object,%20java.lang.Object...%29使用此 Method 对象表示的底层方法调用具有指定的参数。返回值是调用底层方法的返回值。如果基础方法的返回类型为 void,则返回 null。 - JB Nizet
1
你确定要实例化一个新的该类型对象吗?例如,runTheMethod(Object instance, String methodName) 也可以工作,并且由您来创建该类型的对象。 - zapl
@zapl 是的,我以字符串格式获取了类名。 - Crickcoder
可以使用Java反射来实现,但在尝试该解决方案之前,您应该考虑更好的设计。 - Dici
1
@DavidConrad 我明白,但我将在Action/Service/DAO层使用它来根据提供的输入返回值。 - Crickcoder
显示剩余6条评论
3个回答

4

现在该怎么办?

现在你可以像这样在你的carObj上调用方法:

Method method = carObj.getClass().getMethod(methodName);
method.invoke(carObj);

另请参见:

Method.invoke


谢谢您的输入。这个方法很有效。我还有一个问题:如果我想扩展实现,我需要将返回值作为输入传递并进行比较,那么我就需要强制转换或者为CarBean(或我传递的每个类)实现hashCode。对此有什么建议吗?- public runTheMethod(String className, String methodName, Object returnValue) - Crickcoder
是的,您需要进行一些强制转换,然后或者...只需将它们(传递的returnValue和实际返回值)作为Object使用它们的equals方法进行比较(但要小心处理null,以防其中一个或两个都是null)。 - peter.petrov

1
如果你只是想通过名称调用一个方法,那么你不需要将其转换为任何类型。只需使用 Object 进行进一步反射即可。
请参见 Class.getDeclaredMethod(),例如:
Object carObj = ...;
Method method = carObj.getClass().getDeclaredMethod(methodName, ...);
Object retObj = method.invoke(carObj, ...);

请注意,我们不关心carObj的实际类型,尽管如果您想要检查,您可以始终使用instanceofClass.isAssignableFromClass.isInstance。虽然在一个步骤中实例化一个新对象然后调用其方法有点奇怪。一旦runTheMethod返回,该对象就会消失。
顺便说一下,看起来你只是想获取和设置bean属性。你可能想要看看Apache Commons BeanUtils,然后你的示例就变得简单了:
CarBean bean = ...;
String color = (String)PropertyUtils.getSimpleProperty(bean, "color"); // calls getter.

感谢您的输入。我以CarBean为例,但它不仅限于Java Bean。我可能需要在Action/Service/DAO层中运行其他方法。 - Crickcoder
1
注意 getDeclaredMethodgetMethod 的区别,前者只返回在该类中(重新)定义的方法 - 不包括继承而来的方法,后者则仅返回公共方法:http://ideone.com/veKNyU - zapl

1

我怀疑你真的不需要这样做。我猜想你遇到了XY问题。你的问题有点类似于询问Java是否有eval函数,而正确的答案也是相似的

首先要问自己,这些String来自哪里?是程序的另一部分生成的,还是用户提供的输入?

  • 我的程序的另一部分生成了它:因此,您希望程序的一部分决定要执行的操作类型,但不执行操作,并且第二部分执行所选操作。不要生成然后评估字符串,根据您特定的情况使用策略命令构建器设计模式。

  • 这是用户输入:用户可以输入任何内容,包括类和方法名称,当执行时可能会导致程序运行不正常、崩溃、暴露应该保密的信息、损坏持久性信息(例如数据库内容)等等。防止这种情况的唯一方法是解析字符串,然后在成功解析后立即执行方法,或者让解析器创建一个Command对象以供稍后执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接