java - 尝试调用方法时如何使用 Lambda MetaFactory 或方法句柄而不是反射?

标签 java reflection

我有一个方法,假设将类名、参数和方法名作为参数,并根据它在哪个类中找到该方法来调用该方法。该方法:

Method[] methods = className.getDeclaredMethods();
 for (Method meth: methods) {
  if (meth.getName().equalsIgnoreCase(methodName)) {
   try {
    MethodType mt = MethodType.methodType(boolean.class, String.class);
    MethodHandles.Lookup caller = MethodHandles.lookup();
    MethodHandle handle = caller.findVirtual(Utilities.class, meth.getName(), mt);
    /*
     * Trying to invoke using lambdaMetaFactory
     */
    MethodType methodType = MethodType.methodType(boolean.class);
    MethodType invokedType = MethodType.methodType(BooleanSupplier.class);
    /*
     * Throws java.lang.invoke.LambdaConversionException: Incorrect number of parameters for instance method 
     * invokeVirtual com.grainger.Automation.Utilities.navigateToUrl:(String)boolean; 0 captured parameters, 
     * 0 functional interface method parameters, 1 implementation parameters
     */
    CallSite site = LambdaMetafactory.metafactory(caller, "getAsBoolean", invokedType, methodType, handle, methodType);

    MethodHandle factory = site.getTarget();
    BooleanSupplier r = (BooleanSupplier) factory.invoke();
    System.out.println(r.getAsBoolean());
    /*
     * Trying to invoke using Method Handle
     */
    /*
     * Trying to invoke the method handle here, but it fails with:
     * java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(Utilities,String)boolean to (Object)Object
     * methodArguments is an ArrayList<> that is initialized and populated before 
     */
    System.out.println(handle.invokeWithArguments(methodArguments.toArray())); //Output B
    /*
     * Invoking it directly with a string as an argument throws this execption:
     * com.sun.jdi.InvalidTypeException: Generated value (java.lang.String) is not compatible with declared type (java.lang.Object[]). occurred invoking method.
     */
    System.out.println(handle.invokeExact("www.google.com"));
    //                  keywordResult = (Boolean) handle.invoke("www.google.com");
    /*
     * Using the regular Reflections API, the method invoke works but is slow
     */
    //                  keywordResult = (Boolean) meth.invoke(classInstance, methodArguments); //Regular refection call to invoke the function
    break;
   } catch (Throwable e) {
    System.out.println(e.getMessage());
    keywordResult = false;
   }
  }
 }

我尝试调用的方法位于同一包内的单独类中。这是方法定义:

public boolean navigateToUrl(String strUrl) {
return true;
}

当我尝试使用 lambdametafactory 调用该函数时,我收到了 LambdaConversionException。当我尝试使用方法句柄调用方法时,在使用参数调用并在 ArrayList<> 中发送时出现异常 Java.lang.invoke.WrongMethodTypeException。在使用 String 作为参数执行 invokExact 时,我得到 com.sun.jdi.InvalidTypeException。所有这些也列在上面的评论中。

我无法弄清楚我在这里做错了什么。任何帮助将不胜感激!如果他们是我在代码中遗漏的任何东西,请告诉我!

最佳答案

通过 MethodHandle 调用方法通常分为 4 个步骤:

  • 创建查找对象MethodHandles.Lookup将负责创建 MethodHandle;
  • 实例化 MethodType表示方法句柄接受和返回的参数类和返回类型类;
  • 使用给定类的查找和 MethodType 找到一个 MethodHandle
  • 使用一些参数调用MethodHandle

例如,假设您要调用该方法

public boolean navigateToUrl(String strUrl) {
    return true;
}

位于 FooBar 类中。

public void invokeMethod() throws Throwable {
    // 1. Retrieves a Lookup
    MethodHandles.Lookup lookup = MethodHandles.lookup();

    // 2. Creates a MethodType
    MethodType methodType = MethodType.methodType(boolean.class, String.class);
    //                                            ^-----------^  ^----------^
    //                                             return type   argument class

    // 3. Find the MethodHandle
    MethodHandle handle = lookup.findVirtual(FooBar.class, "navigateToUrl", methodType);
    //                                       ^----------^  ^-------------^
    //                                            |        name of method
    //                             class from which method is accessed

    // 4. Invoke the method
    boolean b = (boolean) handle.invokeExact(fooBar, "test");
    //                                       ^----^  ^----^
    //                                          |    argument
    //                       instance of FooBar to invoke the method on

    System.out.println(b);
}

这是一个示例代码:

  • 您可以检索不同的查找,上面给出的查找将可以访问所有方法,包括私有(private)方法。您可以使用 publicLookup() 限制自己使用可公开访问的方法。
  • 通过查找,您可以找到方法,也可以找到字段或构造函数。

可以引用this question (和 this one )以获取有关什么是 MethodHandle 的更多信息。

关于java - 尝试调用方法时如何使用 Lambda MetaFactory 或方法句柄而不是反射?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35904081/

相关文章:

java - 使用 SOAP WebService 与 Java 问题

reflection - 在 config/main.php 中找不到 Yii2 模块(不在/vendor 文件夹下)类

scala - 没有适用于案例类别类型的 TypeTag

java - 如何对带有String、String、String...作为参数的方法进行反射调用?

c# - 使用反射调用具有引用参数的方法

c# - 在 C# 中将控件和变量的状态从一个窗体复制到另一个窗体

java - JavaFX 运行时 JAR 文件 jfxrt.jar 在 Linux 上的位置是什么?

java - OSGi DS : Why is setService called before activate

java - 使用 JSON API 写入存储桶时出现错误 403 "Access not configured"

java - 更新时读取文本文件的值