Java中带有父类参数的getMethod方法

17

给定:

class A
{
    public void m(List l) { ... }
}

假设我想使用反射来调用方法m,并将一个ArrayList作为参数传递给m

List myList = new ArrayList();
A a = new A();
Method method = A.class.getMethod("m", new Class[] { myList.getClass() });
method.invoke(a, Object[] { myList });

第3行的getMethod方法将抛出NoSuchMethodException,因为myList的运行时类型是ArrayList,而不是List。

是否有一种通用的方法可以解决这个问题,而不需要了解类A的参数类型?

4个回答

21

如果您知道类型是 List,则将 List.class 用作参数。

如果您事先不知道类型,请想象您拥有:

public void m(List l) {
 // all lists
}

public void m(ArrayList l) {
  // only array lists
}

如果有自动的方法,反射应该调用哪个方法?

如果你愿意,可以使用Class.getInterfaces()Class.getSuperclass(),但这取决于具体情况。

你可以在这里做的是:

public void invoke(Object targetObject, Object[] parameters,
        String methodName) {
    for (Method method : targetObject.getClass().getMethods()) {
        if (!method.getName().equals(methodName)) {
            continue;
        }
        Class<?>[] parameterTypes = method.getParameterTypes();
        boolean matches = true;
        for (int i = 0; i < parameterTypes.length; i++) {
            if (!parameterTypes[i].isAssignableFrom(parameters[i]
                    .getClass())) {
                matches = false;
                break;
            }
        }
        if (matches) {
            // obtain a Class[] based on the passed arguments as Object[]
            method.invoke(targetObject, parametersClasses);
        }
    }
}

我事先不知道类型。如果我的l.getClass()返回ArrayList作为运行时类型,那么我会在你的示例中期望m2,但也许我期望太多了? - Jonathon Faust
因此,实际获取m1的唯一方法是尝试各种超类和接口的排列组合,这并不是一个很好的想法。 - Jonathon Faust
确切地说,你不想做所有这些排列组合。在一些非常特定的情况下 - 例如构建框架或解释器,或需要与(可能编写不良)第三方库进行接口交互时,需要这样做。然而,生成的代码非常脆弱。反射只是访问类定义的工件的另一种方式。在这种情况下,访问是动态的而不是静态编译的。但需要知道需要调用什么的要求仍然相同。 - luis.espinal
我还要补充一点,如果你真的不知道实际类型,并且要求你在不知道这种类型信息的情况下进行操作,我会说:小心谨慎。一般来说,我会把这种情况看作是代码(或需求)异味,需要重新审视。 - luis.espinal
@Bozho 我想这正是我需要做的 - 我有名称,所以也不必检查类中的每个方法。谢谢! - Jonathon Faust
显示剩余2条评论

2
请参阅java.beans.Expression和java.beans.Statement。

1

为什么不直接传入List.class,而是使用myList.getClass()呢?这正是你的方法所期望的。


1
你需要预先知道你要调用什么。反射不是魔法。你需要知道你想通过反射访问哪个方法名和方法签名。有时候你还需要知道你需要调用哪个版本的方法(例如,你需要访问一个由你的父类定义的方法“m”的版本)。 - luis.espinal
@luis:我同意你的观点,即反射不会帮助你选择方法,但我不同意你需要事先知道要调用哪个确切的方法。在解释器/动态语言环境中,你只有一个方法名、参数,如果你很幸运,你可能会从用户那里得到某种提示。根据参数类型选择适用方法的过程必须由解释器应用层提供,以填补反射API提供的原始材料和选择适用方法之间的差距。 - Jason S
在解释器/动态语言环境中,你(可能)没有方法重载,因此你(可能)可以通过名称来确定应该调用哪个方法。 - Matt Ball
@Matt:嗯?“可能”并不能让你走得太远:在出现过载的情况下,你总是需要选择某些操作,无论是选择第一个方法还是在同名多个方法的情况下失败。至于将方法重载委托给不可能堆栈,只需查看FileInputStream和File的方法/构造函数即可。 - Jason S
1
@Jason - 我关于需要事先知道方法的评论是在Java的上下文中。虽然提到解释器/动态语言的反射能力很有趣,但与OP最初提出问题的上下文无关。 - luis.espinal
显示剩余6条评论

1

我猜你想要使用getDeclaredMethods()。这里有一个示例。你可以浏览方法列表,通过名称选择你想要的方法。但是,这是否健壮或者是个好主意另当别论。


谢谢,我知道这个,但我不想采取这种方式。不过也许我不得不这样做。 - Jonathon Faust

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