使用MethodHandles来调用
在Java中,我们想在程序运行时调用其方法,可以用JDK提供的反射相关API来实现,代码如下:
Class clazz = Demo.class; Method method = clazz.getMethod("studyHard", new Class[]{String.class}); Demo demo = clazz.newInstance(); method.invoke(demo, "哈哈");
这样的代码看起来也挺简洁,不过在JDK7之后,提供了java.lang.invoke.MethodHandle
,它的功能与反射相似,下面是一个例子:
public class Demo { public String test1(int a, String b) { System.out.println("test1 -> " + a + b); return a + b; } public static String test2(int a, String b) { System.out.println("test2 -> " + a + b); return a + b; } public static void main(String[] args) throws Throwable { Class<?> clazz = Demo.class; MethodType signature = MethodType.methodType(String.class, int.class, String.class); MethodHandle mh = MethodHandles.lookup().findVirtual(clazz, "test1", signature); Object obj = clazz.getConstructor().newInstance(); System.out.println(mh.bindTo(obj).invoke(1, "2")); System.out.println((String)mh.bindTo(obj).invokeExact(1, "2")); //--------------静态方法 MethodHandle mh2 = MethodHandles.lookup().findStatic(clazz, "test2", signature); System.out.println(mh.bindTo(obj).invoke(1, "2")); } }
从例子可以看出,我们先要通过MethodType
来生成方法的签名,比如方法的参数类型,方法的返回值类型等。
MethodType signature = MethodType.methodType(String.class, int.class, String.class);
有了方法的签名,我们就可以利用方法签名、方法名称以及该方法所在的类来获取方法句柄(MethodHandle),代码如下:
MethodHandle mh = MethodHandles.lookup().findVirtual(clazz, "test1", signature);
此时需要MethodHandles.lookup()静态方法来查找上下文对象,查找上下文对象有一些以“find”开头的方法,例如,findVirtual()、findSpecial()、findStatic()等。这些方法将会返回实际的方法句柄。
MethodHandle要先bindTo到某个对象实例上,然后调用invoke方法,传入参数就可以调用了。注意还有一个invokeExact方法,该方法与invoke方法的区别是方法参数、返回值匹配非常严格,调用时如果有数值参数隐式转换(如short转int/子类转父类)、装箱拆箱,会直接抛异常。比如函数返回值没有强制转换为String类型,就会抛异常:
Exception in thread "main" java.lang.invoke.WrongMethodTypeException: expected (int,String)String but found (int,String)Object at java.base/java.lang.invoke.Invokers.newWrongMethodTypeException(Unknown Source) at java.base/java.lang.invoke.Invokers.checkExactType(Unknown Source) at MethodHandles/me.mingshan.demo.Demo.main(Demo.java:26)
从目前接触到的API来看,似乎和以前用的反射区别并不大,其实不然,上面的特性包含在JSR 292中,提供了比反射API更加强大的动态方法调用能力,并且新增了一个java虚拟机指令invokedynamic
,invokedynamic
指令通过引导方法(bootstrap method,BSM)机制来使用方法句柄。有关该指令更详细的信息可以参考Java虚拟机规范。
另外,在JDK9中新增了Variable Handles
(变量句柄)相关功能,主要是用来取代java.util.concurrent.atomic
包以及sun.misc.Unsafe
类的功能,在Lookup类中,新增了findVarHandle
方法来获取变量句柄,提供了各种细粒度的原子或有序性操作,更加安全和性能更高,毕竟sun.misc.Unsafe
以后不推荐使用了,不安全。
参考:
title: Use MethodHandles To Invoke
tags: [MethodHandles, java]
author: Mingshan
categories: Java
date: 2018-10-02
本文来自博客园,作者:mingshan,转载请注明原文链接:https://www.cnblogs.com/mingshan/p/17793627.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)