Java 调用最近拟合方法

Java 调用最近拟合方法,java,reflection,dynamic-languages,Java,Reflection,Dynamic Languages,作为开发小型脚本引擎的一部分,我反射性地调用java方法。脚本引擎的调用为对象提供了方法名和参数数组。为了调用该方法,我尝试通过调用Class.getMethod(名称、参数类型)来解析它。 然而,只有当参数的类和方法所期望的类相同时,这才起作用 Object o1 = new Object(); Object out = System.out; //Works as System.out.println(Object) is defined Method ms = out.getClass()

作为开发小型脚本引擎的一部分,我反射性地调用java方法。脚本引擎的调用为对象提供了方法名和参数数组。为了调用该方法,我尝试通过调用Class.getMethod(名称、参数类型)来解析它。
然而,只有当参数的类和方法所期望的类相同时,这才起作用

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()并遍历返回的数组,然后尝试查找fitting方法。然而,如果我不只是想采用我遇到的第一个“好”方法,这将是复杂的,因此我希望有一个现有的解决方案,至少可以处理:

  • 最接近拟合:If arg.getClass()== 子类和方法m(超类), m(子类)然后调用m(子类)
  • 变量参数: System.out.printf(字符串,字符串…)
支持自动装箱也很好。
如果调用无法解析,它可能会抛出异常(ma(字符串,对象),ma(对象,字符串),args=String,String)

(如果你能坚持到这里,谢谢你花时间阅读:-)

好吧,做这种事没有简单的方法。当然,标准Java类库中没有这样做的功能


问题是没有一个“正确”的答案。你需要考虑所有的用例,决定什么是“正确的方法”,并相应地执行反射代码。

< P>我建议你使用。它返回所有公共方法的数组(
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;
}
public方法getMethod(String方法名,类clasz)
{
尝试
{
方法[]methods=clasz.getMethods();
用于(方法:方法)
{
if(methodName.equals(method.getName()))
{
类[]params=method.getParameterTypes();
如果(params.length==1)
{
Class param=params[0];
if((param==int.class)| |(param==float.class)| |(param==float.class))
{
//调用(对象、值);
返回法;
}
else if(参数isAssignableFrom(Number.class))
{
返回法;
}
//否则如果(…)
//{
//    ...
//}
}
}
}
}
捕获(例外e)
{
//一些处理
}
返回null;
}
在本例中,
getMethod(String,Class)
方法将返回一个只包含一个参数的方法,该参数是
int
float
double
,或
Number
的超类

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

然后,您可以通过创建更通用的
getMethod(String,Class)
方法来进一步处理更多可能的“紧密匹配”场景,甚至可能是多个参数



编辑:正如@finnw所指出的,由于其局限性,在使用时要小心,正如我在示例代码中所做的那样,将原语与
Number
对象分开进行测试。

正如其他人所指出的,没有标准方法可以做到这一点,因此您必须实现自己的重载解析算法

尽可能地遵循javac的重载解析规则可能是有意义的:

对于动态类型的脚本语言,您可能可以忽略泛型,但您仍然可以从编译器自动生成的代码中获益

需要注意的一些陷阱:

  • Class.isAssignableFrom
    不知道自动加宽原语转换,因为这些是在编译器中实现的语法转换;它们不会出现在VM或类层次结构中。e、 g.
    int.class.isAssignableFrom(short.class)
    返回
    false
  • 类似地,
    Class.isAssignableFrom
    不知道自动装箱<代码>Integer.class.isAssignable