简介

  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. 【UOJ#450】【集训队作业2018】复读机(生成函数,单位根反演)

    [UOJ#450][集训队作业2018]复读机(生成函数,单位根反演) 题面 UOJ 题解 似乎是\(\mbox{Anson}\)爷的题. \(d=1\)的时候,随便怎么都行,答案就是\(k^n\). ...

  2. Nginx log日志切割shell

    #!/bin/bash#此脚本用于自动分割Nginx的日志,包括access.log和error.log#每天00:00执行此脚本 将前一天的access.log重命名为access-xxxx-xx- ...

  3. 汽车控制器LIMPHOME电路设计

    摘要:本文介绍汽车控制器上常用的3种LIMPHOME电路设计方法,用于在单片机复位重启期间仍能保证外部输出正确性,确保行车安全.    在电子电气领域,单片机使用非常广泛,单片机的复位重启是设计时必须 ...

  4. Spring boot中使用aop详解

      版权声明:本文为博主武伟峰原创文章,转载请注明地址http://blog.csdn.net/tianyaleixiaowu.       aop是spring的两大功能模块之一,功能非常强大,为解 ...

  5. QML学习笔记(六)- 简单计时器和定时器

    做一个简单的qml计时器和定时器,左键触发计时,右键触发定时 GitHub:八至 作者:狐狸家的鱼 本文链接:QML学习笔记(六)- 简单计时器和定时器 左键点击按钮,触发计时器,中键可以暂停计时,同 ...

  6. A1143. Lowest Common Ancestor

    The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U ...

  7. django基于存储在前端的token用户认证

    一.前提 首先是这个代码基于前后端分离的API,我们用了django的framework模块,帮助我们快速的编写restful规则的接口 前端token原理: 把(token=加密后的字符串,key= ...

  8. css的简单学习笔记

    1.CSS的简介 *css :层叠样式表 **层叠: 一层一层. **样式表: 具有大量的属性和属性值 *使得页面的显示效果更加好. *css将网页内容和显示样式进行分离,提高了显示功能. *css不 ...

  9. phpstudy vhosts.conf 文件配置 记录下!

    <VirtualHost _default_:80>DocumentRoot "D:\phpStudy\WWW" <Directory "D:\phpS ...

  10. 一个小误区 JS中的contains

    在Java语言中,contains可以用于判断str1是否包含str2 原生JS中是有contains方法的 但它并不是字符串方法,,仅用于判断DOM元素的包含关系,参数是Element类型 若要在J ...