简介

  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;
}
}

java7 java MethodHandle解析的更多相关文章

  1. Java注解解析-搭建自己的注解处理器(CLASS注解使用篇)

    该文章是继Java注解解析-基础+运行时注解(RUNTIME)之后,使用注解处理器处理CLASS注解的文章.通过完整的Demo例子介绍整个注解处理器的搭建流程以及注意事项,你将知道如何去搭建自己的注解 ...

  2. Java Sax解析

    一.   Java Sax解析是按照xml文件的顺序一步一步的来解析,在解析xml文件之前,我们要先了解xml文件的节点的种类,一种是ElementNode,一种是TextNode.如下面的这段boo ...

  3. Java XML解析工具 dom4j介绍及使用实例

    Java XML解析工具 dom4j介绍及使用实例 dom4j介绍 dom4j的项目地址:http://sourceforge.net/projects/dom4j/?source=directory ...

  4. Java泛型解析(03):虚拟机运行泛型代码

    Java泛型解析(03):虚拟机运行泛型代码      Java虚拟机是不存在泛型类型对象的,全部的对象都属于普通类,甚至在泛型实现的早起版本号中,可以将使用泛型的程序编译为在1.0虚拟机上可以执行的 ...

  5. java socket解析和发送二进制报文工具(附java和C++转化问题)

    解析: 首先是读取字节: /** * 读取输入流中指定字节的长度 * <p/> * 输入流 * * @param length 指定长度 * @return 指定长度的字节数组 */ pu ...

  6. Java XML解析器

    使用Apache Xerces解析XML文档 一.技术概述 在用Java解析XML时候,一般都使用现成XML解析器来完成,自己编码解析是一件很棘手的问题,对程序员要求很高,一般也没有专业厂商或者开源组 ...

  7. java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现

    java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析 ...

  8. java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别

    java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别 目录 java基础解析系列(一)---String.StringBuffer.St ...

  9. java基础解析系列(六)---深入注解原理及使用

    java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer ja ...

随机推荐

  1. 牛客寒假算法基础集训营3处女座和小姐姐(三) (数位dp)

    链接:https://ac.nowcoder.com/acm/contest/329/G来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言52428 ...

  2. SpaceVim中vimproc的vimproc_linux64.so未找到

    vimproc是我使用的SpaceVim中自动安装的插件,在启动时出现了"找不到dll文件"的提示,通过查阅官网( https://github.com/Shougo/vimpro ...

  3. 【洛谷P2709】小B的询问

    题目大意:给定一个长度为 N 的序列,M 个询问,静态查询区间 [l,r] 内的不同颜色数的平方和. 题解:直接莫队即可. 代码如下 #include <bits/stdc++.h> #d ...

  4. 用宏定义代替printf函数

    来自:http://blog.csdn.net/yannanxiu/article/details/52506451 #define _DEBUG_ 1 #if _DEBUG_ #define PR( ...

  5. 跟我一起用node-express搭建一个小项目(node连接mongodb)[三]

    数据库虽然安装并启动成功了,但我们需要连接数据库后才能使用数据库. 怎么才能在 Node.js 中使用 MongoDB 呢? 我们使用官方提供的 node-mongodb-native 驱动模块,打开 ...

  6. python基础之logging

    import logging def logger(): logger = logging.getLogger('logger')#括号里面默认是root logger.setLevel('DEBUG ...

  7. 将vcf文件转化为plink格式并且保持phasing状态

    VCFtools can convert VCF files into formats convenient for use in other programs. One such example i ...

  8. Day3--Python--字符串,for循环,迭代

    常见的基本数据类型: 1.int 整数 主要用来进行数学运算 2.bool 布尔. 判断真假. if 和 while循环中常用 3.str 字符串,一般放小量数据 4.list 列表.可以存放大量的数 ...

  9. Ubuntu下安装Goldendict(翻译软件)

    Ubuntu直接安装 sudo apt-get install goldendict 通过源码安装 失败 下载词典 http://blog.sina.com.cn/s/blog_c58823b3010 ...

  10. appium 切换native/ webview,findby,还有页面元素定位一直小于0的问题的解决

    之前一直有个bug没有解决. 今天,终于解决了. 疑问过程: app是混合应用,项目做了H5优化之后,以前的用例执行总会失败,体现在原来的一个元素点击无反馈 排查原因:1.项目做了H5优化,2.测试的 ...