首页 > 技术文章 > java中的MethodHandler入门

strongmore 2020-07-22 18:57 原文

介绍

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的方法句柄。

推荐阅读