5

作为开发小型 ScriptEngine 的一部分,我反思地调用 java 方法。脚本引擎的调用为对象提供了方法名称和参数数组。为了调用该方法,我尝试通过调用 Class.getMethod(name, argument types) 来解决它。
然而,这仅在参数的类和方法预期的类相同时才有效。

Object o1 = new Object();
Object out = System.out;
//Works as System.out.println(Object) is defined
Method ms = out.getClass().getMethod("println",o1.getClass());
Object o2 = new Integer(4);
//Does not work as System.out.println(Integer) is not defined
Method mo = out.getClass().getMethod("println",o2.getClass());

我想知道是否有一种“简单”的方法来获得正确的方法,如果可能的话最适合参数类型,或者我是否必须自己实现。

最接近的拟合是:

Object o1 = new Integer(1);
Object o2 = new String("");
getMethod(name, o1.getClass())//println(Object)
getMethod(name, o2.getClass())//println(String)  

更新:
澄清我需要什么:脚本引擎是我在空闲时间编写的一个小项目,所以没有我必须遵循的严格规则。所以我认为选择从引擎调用的方法就像java编译器在编译时选择方法一样,只有动态类型而不是对象的静态类型才会起作用。(有或没有自动装箱)
这是我首先希望的Class.getMethod() 会解决。但是 Class.getMethod() 需要与方法声明的参数类型完全相同的类,使用子类将导致无此类方法异常。这可能有充分的理由发生,但使该方法对我来说毫无用处,因为我事先不知道哪些参数类型适合。
另一种方法是调用 Class.getMethods() 并遍历返回的数组并尝试找到合适的方法。然而,如果我不只是想采用我遇到的第一个“好”方法,这将是复杂的,所以我希望有一个至少可以处理的现有解决方案:

  • 最接近的拟合:如果 arg.getClass() == 子类和方法 m(Superclass), m(Subclass) 然后调用 m(Subclass)
  • 可变参数:System.out.printf(String ,String...)

对自动装箱的支持也会很好。
如果无法解决调用,它可能会引发异常( ma(String,Object), ma(Object, String), args= String,String)
(如果您一直到这里,感谢您抽出宝贵时间阅读:- ))

4

3 回答 3

2

我建议你使用getMethods(). Method[]它返回一个包含所有公共方法 ( )的数组。

这里最重要的是:
如果类声明了多个具有相同参数类型的公共成员方法,则它们都包含在返回的数组中。

然后您需要做的是使用此数组中的结果来确定其中哪一个(如果有)是最接近的匹配项。由于最接近的匹配应该在很大程度上取决于您的要求和特定应用程序,因此自己编写代码确实很有意义。


示例代码说明了如何执行此操作的一种方法:

public Method getMethod(String methodName, Class<?> clasz)
{
    try
    {
        Method[] methods = clasz.getMethods();
        for (Method method : methods)
        {
            if (methodName.equals(method.getName()))
            {
                Class<?>[] params = method.getParameterTypes();
                if (params.length == 1)
                {
                    Class<?> param = params[0];
                    if ((param == int.class) || (param == float.class) || (param == float.class))
                    {
                        //method.invoke(object, value);
                        return method;
                    }
                    else if (param.isAssignableFrom(Number.class))
                    {
                        return method;
                    }
                    //else if (...)
                    //{
                    //    ...
                    //}
                }
            }
        }
    }
    catch (Exception e)
    {
        //some handling
    }
    return null;
}

在此示例中,该getMethod(String, Class<?>)方法将返回一个方法,该方法只有一个参数,即intfloatdouble或 的超类Number

这是一个基本的实现——它返回第一个符合要求的方法。您需要扩展它以创建所有匹配方法的列表,然后根据某种标准对它们进行排序,并返回最佳匹配方法。

然后,您可以通过创建更通用的getMethod(String, Class<?>)方法来进一步处理,以处理更多可能的“近距离匹配”场景,甚至可能不止一个参数

高温高压


编辑:正如@finnw 所指出的Class#isAssignableFrom(Class<?> cls),由于它的限制,使用时要小心,就像我在示例代码中所做的那样,将原语与Number对象分开测试。

于 2010-02-09T03:43:09.480 回答
2

正如其他人指出的那样,没有标准方法可以做到这一点,因此您将不得不实现自己的重载解析算法。

尽可能地遵循 javac 的重载解析规则可能是有意义的:http:
//java.sun.com/docs/books/jls/third_edition/html/expressions.html#292575
您可以忽略泛型的动态-类型的脚本语言,但您可能仍会受益于编译器自动生成的桥接方法。

需要注意的一些陷阱:

  • Class.isAssignableFrom不知道自动扩展原语转换,因为这些是在编译器中实现的语法糖;它们不会出现在 VM 或类层次结构中。例如int.class.isAssignableFrom(short.class)返回false
  • 同样Class.isAssignableFrom不知道自动装箱。 Integer.class.isAssignableFrom(int.class)返回false
  • Class.isInstanceClass.cast以 aObject作为论据;您不能将原始值传递给它们。它们还返回一个Object,因此它们不能用于拆箱((int) new Integer(42)在 Java 源代码中是合法的,但int.class.cast(new Integer(42))会引发异常。)
于 2010-02-09T11:34:46.950 回答
1

AFAIK,没有简单的方法来做这种事情。当然,标准 Java 类库中没有任何东西可以做到这一点。

问题是没有单一的“正确”答案。您需要考虑所有用例,确定“正确的方法”应该是什么,并相应地实现反射代码。

于 2010-02-09T01:14:36.053 回答