美文网首页
Java7新特性9-方法句柄

Java7新特性9-方法句柄

作者: 不迷失 | 来源:发表于2017-04-14 17:37 被阅读718次

    方法句柄-现代化的反射

    1f2f7374e1990b4bb551fd141bc4a53d1f2f7374e1990b4bb551fd141bc4a53d

    java7中为间接调用方法引入了新的api,其中最关键的是java.lang.invoke包,即方法句柄。我们可以看成是java反射的升级版,比反射更加灵活、高效。

    方法句柄中首先涉及到两个重要的类,MethodHandle和MethodType。

    MethodHandle

    它是对可直接执行的方法的类型的引用,或者说,它是一个有能力安全调用方法的对象。

    通过句柄我们可以直接调用该句柄所引用的底层方法。从作用上来看,方法句柄类似于反射中的Method类,但是方法句柄的功能更加强大、使用更加灵活、性能也更好。

    MethodType

    它是表示方法签名类型的不可变对象。

    每个方法句柄都有一个MethodType实例,用来指明方法的返回类型和参数类型。

    它完全由参数类型和方法返回类型来确定,而与它所引用的底层的方法的名称和所在的类没有关系。

    举个例子,例如String类的length方法和Integer类的intValue方法的方法句柄的类型就是一样的,因为这两个方法都没有参数,而且返回值类型都是int,则我们可以通过下列语句获取同一个方法类型:

    MethodType mt =MethodType.methodType(int.class);
    

    MethodType的对象实例只能通过MethodType类中的静态工厂方法来创建,参数是一个类型class的动态参数,按照返回值、参数的顺序。

    如果没有参数可以只输入返回值类型的class。

    MethodType.methodType(String.class);
    

    表示方法的返回值是String,无参数。

    使用示例

    package java7;
    
    import java.lang.invoke.MethodHandle;
    import java.lang.invoke.MethodHandles;
    import java.lang.invoke.MethodType;
    
    /**
     * @author qiang.xie
     * @date 2017/4/14
     */
    public class MethodHandlerTest {
    
        public static void main(String[] arg) throws Throwable{
            //获取方法f1的methodType对象,表示此方法的返回值类型和参数类型
            MethodType f1=MethodType.methodType(String.class,int.class);
            MethodType f2=MethodType.methodType(void.class);//无参数,返回值是void的方法
            MethodType f3=MethodType.methodType(void.class);
    
            //通过MethodHandles.lookup()可以在一个类上根据方法名称和方法的methodType获取方法句柄
            //查找普通方法
            MethodHandle mf1=MethodHandles.lookup().findVirtual(MethodHandlerTest.class,"f1",f1);
            MethodHandle mf2=MethodHandles.lookup().findVirtual(MethodHandlerTest.class,"f2",f2);
    
            //查找静态方法
            MethodHandle mf3=MethodHandles.lookup().findStatic(MethodHandlerTest.class,"f3",f3);
            //通过方法句柄调用方法
            MethodHandlerTest methodHandler=new MethodHandlerTest();
            mf1.invoke(methodHandler,123);
            mf2.invoke(methodHandler);
    
            //使用invokeExact调用时,参数类型和返回值类型必须与方法签名的一致
            String v=(String)mf1.invokeExact(methodHandler,1234);
    
            //调用静态方法
            mf3.invoke();
        }
    
    
        public String f1(int a){
            System.out.println("f1"+a);
            return a+"";
        }
    
        public void f2(){
            System.out.println("f2");
        }
    
        public static void f3(){
            System.out.println("f3");
        }
    }
    
    

    运行结果:

    f1123
    f2
    f11234
    f3
    

    示例解析

    首先,在获取方法句柄之前,先通过MethodType的静态工厂方法,先生成一个包含方法返回类型、方法参数类型的方法类型。

    其次,获取方法句柄要用到Lookup对象,这个对象可以提供其所在环境中任何可见方法的方法句柄。

    我们可以将其比喻成包含有某个类对象的方法成员、方法的容器,通过

    lookup.findVirtual(MethodHandlerTest.class,"f1",mt);
    

    查找MethodHandlerTest类型中的f1方法,作为方法句柄返回。

    要从lookup对象中得到方法句柄,需要给出持有所需方法的类,方法的名称,以及跟方法相匹配的方法类型。

    最后,获取到方法句柄后,我们就可以通过方法句柄来调用底层的方法,这点上,跟反射中的方法调用类似。

    方法句柄提供两个方法调用底层方法,invoke和invokeExact方法。

    invokeExact方法在调用时要求严格的类型匹配,方法的返回值类型也在考虑范围之内,只要类型不能匹配就会报错。

    而invoke允许更加松散的调用方式。它会尝试在调用的时候进行返回值和参数类型的转换工作,如果不能正常转换才会报错

    方法的调用参数基本都一样,第一个参数为方法的接受对象,即是哪个对象执行这个方法,接下来的参数就是执行方法所需要的参数。

    这里需要强调一下,静态方法和动态方法之间的差别,静态方法是不需要指定方法的接受对象的,而一般方法是需要的。

    @不迷失|知识改善生活

    weixinweixin
    微信公众号:java技术

    专注技术研究与视频教学,分享有价值的技术与经验,关注程序员的发展!

    --
    技术博客:http://bumishi.cn

    技术交流群:245130488

    @不迷失教学视频

    QQ课堂:http://bumishi.ke.qq.com

    百度传课:http://chuanke.com/s3377987.html

    相关文章

      网友评论

          本文标题:Java7新特性9-方法句柄

          本文链接:https://www.haomeiwen.com/subject/dwqgattx.html