MethodHandle(方法句柄)系列之三:invoke和invokeExact的区别
2024-09-07 16:18:21
先把代码贴上来,用的是一样的代码
/**
*
* @author LiuYeFeng<897908343@qq.com>
* @date 2015年4月8日 下午10:41:13
* @CopyRight 2015 TopView Inc
* @version V1.0
*/
public class MethodHandleTest { public MethodHandle getHandler() {
MethodHandle mh = null;
MethodType mt = MethodType.methodType(String.class, int.class, int.class);
MethodHandles.Lookup lk = MethodHandles.lookup(); try {
mh = lk.findVirtual(String.class, "substring", mt);
} catch (Throwable e) {
e.printStackTrace();
} return mh;
} public static void main(String[] args) throws Throwable {
MethodHandle mh = new MethodHandleTest().getHandler();
String str = "hello world"; Object result1 = mh.invoke(str, , );
Object result2 = (String) mh.invokeExact(str, , );
// Object result2 = mh.invokeExact(str, new Integer(1), 3);
/**
* 上面这句方法执行时报错,因为方法类型为String.class, int.class, int.class
* 而返回的类型为Object,与声明中为String不符合
* 其中第二个参数类型为Integer,与声明中为int不符合,则类型适配不符合,系统报错。
*/ System.out.println("result 1:" + result1);
System.out.println("result 1:" + result2);
}
}
invoke和invokeExact方法的区别,从名字上来看,明显是后者准确性更高,或者说要求更严格。invokeExact方法在调用时要求严格的类型匹配,方法的返回值类型也在考虑范围之内,如同上面代码中注释掉的一句。如果把
Object result2 = (String) mh.invokeExact(str,
1
,
3
);
中的(String)去掉类型转换的话,在调用的时候该方法会认为返回值是Object类型而不是String类型,然后系统报错。
与invokeExact方法不同,invoke方法允许更加松散的调用方式。它会尝试在调用的时候进行返回值和参数类型的转换工作。这是通过MethodHandle类的asType方法来完成的,asType方法的作用是把当前方法句柄适配到新的MethodType上面,并产生一个新的方法句柄。当方法句柄在调用时的类型与其声明的类型完全一致的时候,调用invoke方法等于调用invokeExact方法;否则,invoke方法会先调用asType方法来尝试适配到调用时的类型。如果适配成功,则可以继续调用。否则会抛出相关的异常。这种灵活的适配机制,使invoke方法成为在绝大多数情况下都应该使用的方法句柄调用方式。
进行类型匹配的基本规则是对比返回值类型和每个参数的类型是否都可以相互匹配。假设源类型为S,目标类型为T,则基本规则如下:
1、可以通过java的类型转换来完成,一般从子类转成父类,比如从String到Object类型;
2、可以通过基本类型的转换来完成,只能将类型范围的扩大,比如从int切换到long;
3、可以通过基本类型的自动装箱和拆箱机制来完成,例如从int到Integer;
4、如果S有返回值类型,而T的返回值类型为void,则S的返回值会被丢弃。
5、如果S的返回值是void,而T的返回值是引用类型,T的返回值会是null;
6、如果S的返回值是void,而T的返回值是基本类型,T的返回值会是0;
第1、2、3条很好理解,第4、5、6条感觉原理都一样,我就用个新例子说明。
public class MethodHandleTest { public MethodHandle getHandler() {
MethodHandle mh = null;
MethodType mt = MethodType.methodType(void.class);
MethodHandles.Lookup lk = MethodHandles.lookup(); try {
mh = lk.findVirtual(MethodHandleTest.class, "print", mt);
} catch (Throwable e) {
e.printStackTrace();
} return mh;
} public void print() {
System.out.println("print");
} public static void main(String[] args) throws Throwable {
MethodHandleTest mht = new MethodHandleTest();
MethodHandle mh = mht.getHandler(); int result1 = (int) mh.invoke(mht);
Object result2 = mh.invoke(mht); System.out.println("result 1:" + result1);
System.out.println("result 2:" + result2);
}
}
程序输出结果是:
print
print
result 1:0
result 2:null
参考资料:《java程序员修炼之道》、《深入理解java7核心技术与最佳实践》
最新文章
- slave IO流程之一:mysql登陆过程(mysql_real_connect)
- [异常解决] android studio检测不到手机的解决办法——ADB驱动自己安装
- yum clean all 是什么意思
- POJ 2155 Matrix
- ext grid 子表格
- 各种有用的PHP开源库精心收集
- Effective Java 42 Use varargs judiciously
- MQ的通讯模式
- Landsat8免费下载地址
- 为hbase新增节点
- Sqlite数据库简介
- 点击按钮改变标签内容(采用lambda函数方式)
- Oracle - index (索引)
- python 集合的操作
- 使用sed替换指定文件指定行的指定文本
- HDU4283(KB22-G)
- tableview中头部信息
- word2vec原理CBOW与Skip-Gram模型基础
- hdu1010 Tempter of the Bone(深搜+剪枝问题)
- bzoj 4540: [Hnoi2016]序列
热门文章
- HTTP GET | POST | DELETE请求
- JAVA基础篇 之 finalize()方法的作用
- 【Hadoop离线基础总结】MapReduce参数优化
- STM32 ADC多通道规则采样和注入采样
- 想要年薪百万,阿里Sentinel支持RESTful接口都搞不定?
- chrome安装工具
- 一篇文章教会你利用Python网络爬虫获取电影天堂视频下载链接
- (Python基础教程之八)Python中的list操作
- PAT 1001 A+B Format (20分) to_string()
- Nginx 、MySQL、Django 在 Docker-compose 中的部署