2016-03-09 16 views
1

我有一個想採取的類名,論證的方法和方法名作爲參數,並調用該方法依賴於它發現它在類中的方法:如何在嘗試調用方法時使用Lambda MetaFactory或方法處理而不是反射?

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異常。所有這些也都在上面的評論中列出。

我無法弄清楚我在這裏做錯了什麼。任何幫助將不勝感激!如果他們是我從代碼中遺漏的任何東西,請告訴我!

回答

5

通過MethodHandle調用一個方法通常會在4個步驟:

  • 創建一個查找對象MethodHandles.Lookup將負責創建MethodHandle;
  • Instantiante a MethodType它表示參數類和返回類型類接受並由方法句柄返回;
  • 找到一個MethodHandle與查找和MethodType在給定的類;
  • 用一些參數調用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); 
} 

這是一個示例代碼:

  • 可以獲取不同的查詢,上面給出的一個將有機會獲得每一個方法包括不公開的。您可以使用publicLookup()將自己限制爲可公開訪問的方法。
  • 通過查找,您可以查找方法,也可以查找字段或構造函數。

您可以參考this question(和this one)有關哪些MethodHandle s爲更多信息。

+0

This Works!我相信唯一缺少的就是將類的實例作爲參數傳遞給invokeExact和invokeWithArguments。我是否也可以使用LambdaMetaFactory來完成此操作?性能和代碼運行時間是我需要考慮的事情,在進行我的研究時,我發現Lambdametafactory可以比反射或方法處理快得多。 –

+0

@SahilGupta方法處理將比反射有更多的性能是!'Lambdametafactory'依賴'MethodHandle',所以它是一樣的。 – Tunaki

+0

所以他們沒有太大的區別,我是否使用'Lambdametafactory'和'MethodHandle'?我嘗試使用上面的'lambdametafactory',通過發送'booleanSupplier'函數接口並使用'Methodhandle'來查找函數。你認爲我應該堅持我所擁有的還是嘗試使用「Lambdametafactory」?即使使用「Lambdametafactory」,速度只有一秒,但我更傾向於使用它。 –