反射优化之方法句柄

简介

对于反射,相信从业 java 开发的人多多少少有些耳闻,在 jdk 1.7 为了间接调用方法引入了另一个新的 API ,即方法句柄。在方法句柄里面,有两个重要的类分别是 MethodType 和 MethodHandle,下面允许我做个名词解释。

  • MethodType

方法签名不可变对象,即对方法的一个映射,包含返回值和参数类型。在 lookup 时也是通过它来寻找的。
每个方法句柄都有个 MethodType 的实例,用来指明方法的返回类型和参数类型。

  • MethodHandle

通过句柄我们可以直接调用该句柄所引用的底层方法。从作用上来看,方法句柄类似于反射中的 Method 类,是对要执行的方法的一个引用,我们也是通过它来调用底层方法,它调用时有两个方法 invoke 和 invokeExact,后者要求参数类型与底层方法的参数完全匹配,前者则在有出入时做修改如包装类型。

示例

下面用一个简易的程序去测试句柄


package com.wwjd.dao.interfaces;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.time.Duration;
import java.time.Instant;

/**
 * 句柄测试
 *
 * @author 阿导
 * @CopyRight 万物皆导
 * @Created 2019年02月25日 10:16:00
 */
public class MethodHandleDemo {

    public static void main(String[] args) throws Throwable {
        // 参数为返回值类型、参数类型 单个参数
        MethodType methodType =MethodType.methodType(void.class,String.class);
        // 声明定义的方法句柄,通过 lookup 对象得到方法句柄,参数为方法所在的类、方法名称,所匹配的方法签名
        MethodHandle methodHandle = MethodHandles.lookup().findVirtual(Dao.class,"dao",methodType);
        // 调用底层方法
        methodHandle.invoke(Dao.class.newInstance(),"万物皆导");


        Instant instant =Instant.now();

        MethodHandle methodHandle1 = MethodHandles.lookup().findVirtual(Dao.class,"dao",MethodType.methodType(Integer.class,String.class,int.class));

        Object o = methodHandle1.invoke(Dao.class.newInstance(), "万物皆导", 1);
        System.out.println(o);

        Duration duration =Duration.between(instant,Instant.now());
        System.out.println(duration.toMillis());
        // 若是静态方法
        MethodHandle methodHandleStatic = MethodHandles.lookup().findStatic(Dao.class,"add",MethodType.methodType(Object.class,int.class,int.class));

        Object invoke = methodHandleStatic.invoke(10, 20);
        System.out.println(invoke);
    }
}



class Dao{
    public void dao(String dao){
        System.out.println("dao:"+dao);
    }

    public Integer dao(String dao,int index){
        System.out.println("dao:"+dao+",index:"+index);
        return index;
    }

    public static Object add(int a,int b){
        System.out.println("a+b="+(a+b));
        return a+b;
    }
}

虽然相比反射而言,句柄实现起来会更加简单,但是 MethodHandle 获得的方法引用,并不能突破访问权限本身的限制,比如private方法,就不能在类外被使用,这一点不如反射。另外 invoke 和 invokeExact 的区别如下:

  • invokeExtract 要求更加精确 ,invoke方法允许更加松散的调用方式

因为 invoke 会做一些适配,如参数类型的转换,但 invokeExtract 则不会,所以为了书写方便尽可能的使用 invoke。

反射和句柄的区别如下

  • Reflection 和 MethodHandle 机制本质上都是在模拟方法调用,但是 Reflection 是在模拟 Java 代码层次的方法调用,而 MethodHandle 是在模拟字节码层次的方法调用
  • Reflection 中的 Method 对象远比 MethodHandle 机制中的 MethodHandle 对象所包含的信息要多。前者是方法在Java一端的全面映像,包含了方法的签名、描述符以及方法属性表中各种属性的 Java 端表示方式,还包含有执行权限等的运行期信息。而后者仅仅包含着与执行该方法相关的信息。通俗的话说,Reflection 是重量级,而 MethodHandle 是轻量级
  • 由于 MethodHandle 是对字节码的方法指令调用的模拟,那理论上虚拟机在这方面做的各种优化(如方法内联),在 MethodHandle 上也应当可以采用类似思路去支持(但目前实现还不完善)。而通过反射去调用方法则不行
  • Reflection API 的设计目标是只为 Java 语言服务的,而 MethodHandle 则设计为可服务于所有 Java 虚拟机之上的语言,其中也包括了 Java 语言
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值