java MethodHandle解析

sunjiaminaini發表於2017-08-09

簡介
java7在JSR 292中增加了對動態型別語言的支援,使Java也可以像C語言那樣將方法作為引數傳遞,其實現在lava.lang.invoke包中。MethodHandle作用類似於反射中的Method類,但它比Method類要更加靈活和輕量級。通過MethodHandle進行方法呼叫一般需要以下幾步:
(1)建立MethodType物件,指定方法的簽名;
(2)在MethodHandles.Lookup中查詢型別為MethodType的MethodHandle;
(3)傳入方法引數並呼叫MethodHandle.invoke或者MethodHandle.invokeExact方法。

MethodType
可以通過MethodHandle類的type方法檢視其型別,返回值是MethodType類的物件。也可以在得到MethodType物件之後,呼叫MethodHandle.asType(mt)方法適配得到MethodHandle物件。可以通過呼叫MethodType的靜態方法建立MethodType例項,有三種建立方式:
(1)methodType及其過載方法:需要指定返回值型別以及0到多個引數;
(2)genericMethodType:需要指定引數的個數,型別都為Object;
(3)fromMethodDescriptorString:通過方法描述來建立。
建立好MethodType物件後,還可以對其進行修改,MethodType類中提供了一系列的修改方法,比如:changeParameterType、changeReturnType等。

Lookup
MethodHandle.Lookup相當於MethodHandle工廠類,通過findxxx方法可以得到相應的MethodHandle,還可以配合反射API建立MethodHandle,對應的方法有unreflect、unreflectSpecial等。

invoke
在得到MethodHandle後就可以進行方法呼叫了,有三種呼叫形式:
(1)invokeExact:呼叫此方法與直接呼叫底層方法一樣,需要做到引數型別精確匹配;
(2)invoke:引數型別鬆散匹配,通過asType自動適配;
(3)invokeWithArguments:直接通過方法引數來呼叫。其實現是先通過genericMethodType方法得到MethodType,再通過MethodHandle的asType轉換後得到一個新的MethodHandle,最後通過新MethodHandle的invokeExact方法來完成呼叫。

附MethodHandle作為引數的示例程式碼:

package com.sun.jojo.methodhandle;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import java.util.List;

/**
 * Author sunjiamin
 * Date 2017/8/9 11:14
 * Describe :
 */
public class MethodHandleTest {

    public static void main(String[] args) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle methodHandle =  lookup.findStatic(MethodHandleTest.class,"doubleVal", MethodType.methodType(int.class,int.class));
        List<Integer> dataList = Arrays.asList(1, 2, 3, 4, 5);
        MethodHandleTest.transform(dataList,methodHandle);
        for (Integer data : dataList) {
            System.out.println(data);//2,4,6,8,10
        }

    }


    public static List<Integer> transform(List<Integer> dataList, MethodHandle handle) throws Throwable {
        for (int i = 0; i < dataList.size(); i++) {
            dataList.set(i, (Integer) handle.invoke(dataList.get(i)));//invoke
        }
        return dataList;
    }

    /**
     * 作為引數的方法
     * @param val
     * @return
     */
    public static int doubleVal(int val) {
        return val * 2;
    }
}

相關文章