java中的MethodHandler入门
介绍
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的方法句柄。