zoukankan      html  css  js  c++  java
  • java7 java MethodHandle解析

    简介

      JDK6之前我们会使用java反射来实现动态方法调用,多数框架用反射的比较多,例如mybatis、spring等。在JDK7中,新增了java.lang.invoke.MethodHandle(方法句柄),称之为“现代化反射”。其实反射和java.lang.invoke.MethodHandle都是间接调用方法的途径,但java.lang.invoke.MethodHandle比反射更简洁,用反射功能会写一大堆冗余代码。

           官方api给出的解释:

           A method handle is a typed, directly executable reference to an underlying method, constructor, field, or similar low-level operation, with optional transformations of arguments or return values.其实就是可以获取方法的句柄,类似方法的指针。

          下面看一个例子,使用方法句柄(MethodHandle)调用toString()方法:

    package org.zwc.methodhandletest;
     
    import java.lang.invoke.MethodHandle;
    import java.lang.invoke.MethodHandles;
    import java.lang.invoke.MethodType;
     
     
    public class MHTest {
     
     
        public String toString(String s) {
     
            return "hello," + s + "MethodHandle";
        }
     
     
        public static void main(String[] args) {
            MHTest mhTest = new MHTest();
            MethodHandle mh = getToStringMH();  //获取方法句柄
           
            try {
                // 1.调用方法:
                String result = (String) mh.invokeExact(mhTest, "ssssss");  //根据方法句柄调用方法----注意返回值必须强转
                System.out.println(result);
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
     
     
            // 2.or like this:
            try {
                MethodHandle methodHandle2 = mh.bindTo(mhTest);
                String toString2 = (String) methodHandle2.invokeWithArguments("sssss");
                System.out.println(toString2);
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
     
     
            // 得到当前Class的不同表示方法,最后一个最好。一般我们在静态上下文用SLF4J得到logger用。
            System.out.println(MHTest.class);
            System.out.println(mhTest.getClass());
            System.out.println(MethodHandles.lookup().lookupClass()); // like getClass()
     
     
        }
     
        /**
         * 获取方法句柄
         * @return
         */
        public static MethodHandle getToStringMH() {
     
            MethodType mt = MethodType.methodType(String.class, String.class);  //获取方法类型 参数为:1.返回值类型,2方法中参数类型
     
            MethodHandle mh = null;
            try {
                mh = MethodHandles.lookup().findVirtual(MHTest.class, "toString", mt);  //查找方法句柄
            } catch (NoSuchMethodException | IllegalAccessException e) {
                e.printStackTrace();
            }
     
            return mh;
        }
    }
    执行结果:
    hello,ssssssMethodHandle
    hello,sssssMethodHandle
    class org.zwc.methodhandletest.MHTest
    class org.zwc.methodhandletest.MHTest
    class org.zwc.methodhandletest.MHTest

    java7在JSR 292中增加了对动态类型语言的支持,使Java也可以像C语言那样将方法作为参数传递,其实现在lava.lang.invoke包中。MethodHandle作用类似于反射中的Method类,但它比Method类要更加灵活和轻量级。通过MethodHandle进行方法调用一般需要以下几步:
    (1)创建MethodType对象,指定方法的签名;
    (2)在MethodHandles.Lookup中查找类型为MethodType的MethodHandle;
    (3)传入方法参数并调用MethodHandle.invoke或者MethodHandle.invokeExact方法。

    MethodType
    可以通过MethodHandle类的type方法查看其类型,返回值是MethodType类的对象。也可以在得到MethodType对象之后,调用MethodHandle.asType(mt)方法适配得到MethodHandle对象。可以通过调用MethodType的静态方法创建MethodType实例,有三种创建方式:
    (1)methodType及其重载方法:需要指定返回值类型以及0到多个参数;
    (2)genericMethodType:需要指定参数的个数,类型都为Object;
    (3)fromMethodDescriptorString:通过方法描述来创建。
    创建好MethodType对象后,还可以对其进行修改,MethodType类中提供了一系列的修改方法,比如:changeParameterType、changeReturnType等。

    Lookup
    MethodHandle.Lookup相当于MethodHandle工厂类,通过findxxx方法可以得到相应的MethodHandle,还可以配合反射API创建MethodHandle,对应的方法有unreflect、unreflectSpecial等。

    invoke
    在得到MethodHandle后就可以进行方法调用了,有三种调用形式:
    (1)invokeExact:调用此方法与直接调用底层方法一样,需要做到参数类型精确匹配;
    (2)invoke:参数类型松散匹配,通过asType自动适配;
    (3)invokeWithArguments:直接通过方法参数来调用。其实现是先通过genericMethodType方法得到MethodType,再通过MethodHandle的asType转换后得到一个新的MethodHandle,最后通过新MethodHandle的invokeExact方法来完成调用。

    附MethodHandle作为参数的示例代码:

    package com.sun.jojo.methodhandle;
    
    import java.lang.invoke.MethodHandle;
    import java.lang.invoke.MethodHandles;
    import java.lang.invoke.MethodType;
    import java.util.Arrays;
    import java.util.List;
    
    public class MethodHandleTest {
    
        public static void main(String[] args) throws Throwable {
            MethodHandles.Lookup lookup = MethodHandles.lookup();
            MethodHandle methodHandle =  lookup.findStatic(MethodHandleTest.class,"doubleVal", MethodType.methodType(int.class,int.class));
            List<Integer> dataList = Arrays.asList(1, 2, 3, 4, 5);
            MethodHandleTest.transform(dataList,methodHandle);
            for (Integer data : dataList) {
                System.out.println(data);//2,4,6,8,10
            }
        }
    
        public static List<Integer> transform(List<Integer> dataList, MethodHandle handle) throws Throwable {
            for (int i = 0; i < dataList.size(); i++) {
                dataList.set(i, (Integer) handle.invoke(dataList.get(i)));//invoke
            }
            return dataList;
        }
    
        /**
         * 作为参数的方法
         * @param val
         * @return
         */
        public static int doubleVal(int val) {
            return val * 2;
        }
    }
  • 相关阅读:
    解决ssh: connect to host github.com port 22: Connection refused
    sudo/su命令免密执行
    Linux tar.gz、tar、bz2、zip 等解压缩、压缩命令详解
    项目实战——校验文件名和邮箱地址
    项目实战:判断闰年平年并输出某月的天数
    ubuntu软件更新时出现没有Release文件
    项目实战:根据出生日期判断星座
    项目实战--实现淡旺季飞机票打折
    xxx.java:1: 错误: 类xxx是公共的, 应在名为 xxx.java 的文件中声明 public class xxx
    32位int整数范围
  • 原文地址:https://www.cnblogs.com/guchunchao/p/10578816.html
Copyright © 2011-2022 走看看