介紹
MethodHandler,翻譯過來就是方法句柄,是java7提供的jsr292的一部分,為了支持動態方法的調用,主要是java.lang.invoke包。
使用
public class Client {
public static void main(String[] args) throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
//獲取String類中靜態方法valueOf對應的方法句柄
MethodHandle valueOfMethodHandler = lookup.findStatic(String.class, "valueOf", MethodType.methodType(String.class, int.class));
//執行方法句柄
String result = (String) valueOfMethodHandler.invokeExact(12);
System.out.println(result);
}
}
Lookup可以簡單看做查找方法句柄的工具,MethodType表示一個方法類型,包括返回值和參數列表。方法句柄相對於反射來說,更加的輕量級。
動態調用點(CallSite)
java7新增了一個字節碼invokedynamic,可以在運行期動態決定調用的方法,區別於之前的invokestatic(靜態方法調用),invokespecial(構造方法,私有方法,父類方法),invokevirtual(實例方法),invokeinterface(接口方法),不過在java7下javac不支持生成invokedynamic,java8中可以通過lambda來生成。
public class Client {
public static void main(String[] args) throws Throwable {
MethodType type = MethodType.methodType(String.class, int.class, int.class);
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = lookup.findVirtual(String.class, "substring", type);
ConstantCallSite callSite = new ConstantCallSite(handle);
MethodHandle invoker = callSite.dynamicInvoker();
String result = (String) invoker.invoke("Hello", 0, 3);
System.out.println(result);
}
}
以上一個CallSite的小例子,會輸出Hel。
public class Client {
public static void main(String[] args) {
updateAfterPay(info -> System.out.println(info));
}
private static void updateAfterPay(Consumer<String> consumer) {
System.out.println("start ...");
consumer.accept("pay success");
System.out.println("end ...");
}
}
這是lambda表達式的一個小例子,我們看一下反編譯后的結果
public class Client {
public static void main(String[] args) { Client.updateAfterPay((Consumer<String>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)V, lambda$main$0(java.lang.String ), (Ljava/lang/String;)V)());
}
private static void updateAfterPay(Consumer<String> consumer) {
System.out.println("start ...");
consumer.accept("pay success");
System.out.println("end ...");
}
//這個方法是編譯器幫我們創建的,就是lambda處理的內容
private static /* synthetic */ void lambda$main$0(String info) {
System.out.println(info);
}
}
看一下LambdaMetafactory的文檔介紹
Methods to facilitate the creation of simple "function objects" that
* implement one or more interfaces by delegation to a provided {@link MethodHandle},
* possibly after type adaptation and partial evaluation of arguments. These
* methods are typically used as <em>bootstrap methods</em> for {@code invokedynamic}
* call sites, to support the <em>lambda expression</em> and <em>method
* reference expression</em> features of the Java Programming Language.
可以簡單理解為一個創建動態調用點CallSite的工具,metafactory方法的返回值就是CallSite。
通過javap看一下反編譯的字節碼
從這我們可以看出lambda表達式的實現原理大概就是將lambda轉換成一個動態調用點的調用,動態調用點又會代理給方法句柄MethodHandle,在我們這個例子中就是lambda$main$0的方法句柄。