【问题标题】:Calling closest fitting method调用最接近拟合方法
【发布时间】:2011-01-14 14:57:13
【问题描述】:

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

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(超类), m(Subclass) 然后调用 m(Subclass)
  • 变量参数: System.out.printf(String ,String...)

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

【问题讨论】:

  • 先为“最接近”定义严格的规则,然后是细节。
  • 这也是我想知道的。但是对于所有迂腐正确的回答者来说,“定义最接近”,我有一个简单的答案:Java 通常会选择的答案(我猜是类加载)。

标签: java reflection dynamic-languages


【解决方案1】:

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

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

需要注意的一些陷阱:

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

【讨论】:

    【解决方案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&lt;?&gt;) 方法将返回一个方法,该方法只有一个参数,即 intfloatdoubleNumber 的超类。

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

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

    HTH


    编辑:正如@finnw 指出的那样,由于其局限性,在使用Class#isAssignableFrom(Class&lt;?&gt; cls) 时要小心,正如我在示例代码中所做的那样,将原语与Number 对象分开测试。

    【讨论】:

    • @bguiz,不错的解决方案。只想补充一点,您可以测试Class.isPrimitive()Class.isArray() 以解决isAssignableFrom() 的一些限制。 @finnw 的回答也是。
    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 2020-01-23
      • 2011-04-07
      • 1970-01-01
      • 1970-01-01
      • 2013-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多