Method Handles in Java

1.介绍

在本文中,我们将探讨一个重要的API,它是在Java 7中引入的,并在Java 7版本之后更加完善:全限定名是:Java.lang.invoke.MethodHandles

我们将学习什么是method handles,如何创建它们和如何使用他们。

2.什么是MethodHandle

MethodHandle来自Oracle java7的定义:

"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. These transformations are quite general, and include such patterns as conversion, insertion, deletion, and substitution. "

这个定义暗示着:MethodHandle:

  • 方法的引用。和c++/c中不大相同的是,是对引用的方法的限制:即引用的方法和MethodHandle的type(参数和返回值)必须一样。
  • Executable。An Methodhandle can be executed by invoking its methods: invokeExact..
  • Method Type transformations.

    简单地说,方法句柄是low-level mechanism(为了查找、调整和调用方法)。

    a low-level mechanism for finding, adapting and invoking methods.

创建和使用一个MethodHandle,需要进行一下四个步骤:

  • Creating the lookup // 创建lookup
  • Creating the method type // 创建method type
  • Finding the method handle // 找到method handle
  • Invoking the method handle // 调用

3. Method Handles vs Reflection

Method Handles的引入是为了与已经存在的java.lang.reflect API相配合。他们分别是为了解决不同的问题而出现的。从性能角度上说,MethodHandle api要比反射快很多因为访问检查在创建的时候就已经完成了,而不是像反射一样等到运行时候才检查。但同时,Method Handles比反射更难用,因为没有列举类中成员,获取属性访问标志之类的机制。

另外,MethodHandles可以操作方法,更改方法参数的类型和他们的顺序。而反射则没有这些功能。

从以上角度看,反射更通用,但是安全性更差,因为可以在不授权的情况下使用反射对象。而method Handles遵从了分享者的能力。所以method handle是一种更低级的发现,适配和调用方法的方式,唯一的优点就是更快。所以反射更适合主流Java开发者,而method handle更适用于对编译和运行性能有要求的人。

4.创建method handle

要使用method handle,首先需要得到Lookup。这是创造方法,构造函数,属性的method handles的工厂类。

// public方法的Lookup
MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
// 所有方法的Lookup
MethodHandles.Lookup lookup = MethodHandles.lookup();

5.创建MethodType

要创建MethodHandle,lookup需要一个定义了它的类型的MethodType对象。这里的类型包括了传入参数的类型,和最后返回的类型,要一一对应。第一个是返回类型,如果没有返回值就是Void.class, 后面是可变的传入参数的类型。

a MethodType represents the arguments and return type accepted and returned by a method handle or passed and expected by a method handle caller.

such as

// 接收数组,返回一个List对象,其中Object[].class作为input type 即入参类型
MethodType mt = MethodType.methodType(List.class, Object[].class);

6.查找MethodHandle

Lookup之所以叫Lookup自然是因为他们有查找MethodHandle的能力。先看看他的方法。

Once we've defined our method type, in order to create a MethodHandle, we have to find it through the lookup or publicLookup object, providing also the origin class and the method name.

In particular, the lookup factory provides a set of methods that allow us to find the method handle in an appropriate way considering the scope of our method.

6.1 Method Handle for Methods

Using the findVirtual() method allow us to create a MethodHandle for an object method. Let's create one, based on the concat() method of the String class:

// 上面说过第一个入参为返回类型,后面是可变的传入参数的类型。
MethodType mt = MethodType.methodType(String.class, String.class); // 使用lookup的findVirtual函数可以获得一个对象方法的MethodHandle 。查找 the concat() method of the String class
MethodHandle concatMH = publicLookup.findVirtual(String.class, "concat", mt);

6.2.Method Handle for Static Methods

When we want to gain access to a static method, we can instead use the findStatic() method:

MethodType mt = MethodType.methodType(List.class, Object[].class);

MethodHandle asListMH = publicLookup.findStatic(Arrays.class, "asList", mt);

In this case, we created a method handle that converts an array of Objects to a List of them.

6.3. Method Handle for Constructors

Gaining access to a constructor can be done using the findConstructor() method.

Let's create a method handles that behaves as the constructor of the Integer class, accepting a String attribute:

MethodType mt = MethodType.methodType(void.class, String.class);

MethodHandle newIntegerMH = publicLookup.findConstructor(Integer.class, mt);

6.4.Method Handle for Fields

Using a method handle it's possible to gain access also to fields.

Let's start defining the Book class:

public class Book {

    String id;
String title; // constructor }

Havinga as precondition direct access visibility between the method handle and the declared property , we can create a method handle that behaves as a getter:

MethodHandle getTitleMH = lookup.findGetter(Book.class, "title", String.class);

For further information on handling variables/fields, give a look at the Java 9 Variable Handles Demystified, where we discuss the java.lang.invoke.VarHandle API, added in Java 9.

6.5. Method Handle for Private Methods

Creating a method handle for a private method can be done, with the help of the java.lang.reflect API.

Let's start adding a private method to the Book class:

private String formatBook() {
return id + " > " + title;
}

Now we can create a method handle that behaves exactly as the formatBook() method:

Method formatBookMethod = Book.class.getDeclaredMethod("formatBook");
formatBookMethod.setAccessible(true);
MethodHandle formatBookMH = lookup.unreflect(formatBookMethod);

7. Invoking a Method Handle -- 方法句柄的调用

在获取到了一个方法句柄之后,最直接的使用方法就是调用它所引用的底层方法。在这点上,方法句柄的使用类似于反射API中的Method类。但是方法句柄在调用时所提供的灵活性是Method类中的invoke方法所不能比的。

Once we've created our method handles, use them is the next step. In particular, the MethodHandle class provides 3 different way to execute a method handle: invoke(), invokeWithArugments() and invokeExact().

7.1. Invoking a Method Handle

When using the invoke() method, we enforce the number of the arguments (arity) to be fixed

(我们强制了固定参数的数量)but we allow the performing of casting and boxing/unboxing of the arguments and return types(但是我们允许对参数和返回类型进行强制转换和装箱/拆箱。).

Let's see how it's possible to use the invoke() with a boxed argument:

MethodType mt = MethodType.methodType(String.class, char.class, char.class);
MethodHandle replaceMH = publicLookup.findVirtual(String.class, "replace", mt); String output = (String) replaceMH.invoke("jovo", Character.valueOf('o'), 'a');
assertEquals("java", output);

In this case, the replaceMH requires char arguments, the invoke() performs an unboxing on the Character argument before its execution.

7.2. Invoking With Arguments

Invoking a method handle using the invokeWithArguments method, is the least restrictive of the three options.

In fact, it allows a variable arity invocation, in addition to the casting and boxing/unboxing of the arguments and of the return types.

Coming to practice, this allows us to create a List of Integer starting from an array of int values:


MethodType mt = MethodType.methodType(List.class, Object[].class); MethodHandle asList = publicLookup.findStatic(Arrays.class, "asList", mt); List<Integer> list = (List<Integer>) asList.invokeWithArguments(1,2); assertThat(Arrays.asList(1,2), is(list));

7.3. Invoking Exact

In case we want to be more restrictive in the way we execute a method handle (number of arguments and their type), we have to use the invokeExact() method.

In fact, it doesn't provide any casting to the class provided and requires a fixed number of arguments.

Let's see how we can sum two int values using a method handle:

MethodType mt = MethodType.methodType(int.class, int.class, int.class);
MethodHandle sumMH = lookup.findStatic(Integer.class, "sum", mt); int sum = (int) sumMH.invokeExact(1, 11); assertEquals(12, sum);

If in this case, we decide to pass to the invokeExact method a number that isn't an int, the invocation will lead to WrongMethodTypeException.

方法调用细则:

有三种方法可以调用方法invoke(), invokeWithArugments()和invokeExact(),当我们使用invoke时,我们必须固定arguments的数目。

具体的区别是:

与invokeExact方法不同,invoke方法允许更加松散的调用方式。它会尝试在调用的时候进行返回值和参数类型的转换工作

这是通过MethodHandle类的asType方法来完成的,asType方法的作用是把当前方法句柄适配到新的MethodType上面,并产生一个新的方法句柄。当方法句柄在调用时的类型与其声明的类型完全一致的时候,调用invoke方法等于调用invokeExact方法;否则,invoke方法会先调用asType方法来尝试适配到调用时的类型。如果适配成功,则可以继续调用。否则会抛出相关的异常。这种灵活的适配机制,使invoke方法成为在绝大多数情况下都应该使用的方法句柄调用方式。

参考:

https://www.baeldung.com/java-method-handles

https://www.cnblogs.com/night-wind/p/4405564.html

https://blog.csdn.net/lyj1597374034/article/details/105250459

参考资料:《java程序员修炼之道》、《深入理解java7核心技术与最佳实践》

java方法句柄-----4.你所不知道的MethodHandle【翻译】的更多相关文章

  1. 你所不知道的库存超限做法 服务器一般达到多少qps比较好[转] JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2.1延迟加载(Lazy Loading) EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制

    你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...

  2. 你所不知道的五件事情--java.util.concurrent(第二部分)

    这是Ted Neward在IBM developerWorks中5 things系列文章中的一篇,仍然讲述了关于Java并发集合API的一些应用窍门,值得大家学习.(2010.06.17最后更新) 摘 ...

  3. Android中Context详解 ---- 你所不知道的Context

    转自:http://blog.csdn.net/qinjuning/article/details/7310620Android中Context详解 ---- 你所不知道的Context 大家好,  ...

  4. 你所不知道的 URL

    0.说明 第一幕 产品:大叔有用户反映账户不能绑定公众号.大叔:啊咧咧?怎么可能,我看看?大叔:恩?这也没问题啊,魏虾米.大叔:还是没问题啊,挖叉类.大叔:T T,话说产品姐姐是不是Java提供接口的 ...

  5. Android Context完全解析,你所不知道的Context的各种细节

    Context相信所有的Android开发人员基本上每天都在接触,因为它太常见了.但是这并不代表Context没有什么东西好讲的,实际上Context有太多小的细节并不被大家所关注,那么今天我们就来学 ...

  6. Android中Context详解 ---- 你所不知道的Context(转)

    Android中Context详解 ---- 你所不知道的Context(转)                                               本文出处 :http://b ...

  7. 一些你所不知道的VS Code插件

    摘要: 你所不知道的系列. 原文:提高 JavaScript 开发效率的高级 VSCode 扩展之二! 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 作为一名业余爱好者.专业人员,甚 ...

  8. 你所不知道的setInterval

    在你所不知道的setTimeout记载了下setTimeout相关,此篇则整理了下setInterval:作为拥有广泛应用场景(定时器,轮播图,动画效果,自动滚动等等),而又充满各种不确定性的这set ...

  9. 你所不知道的setTimeout

    JavaScript提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout()和setInterval()这两个函数来完成.它们向任务队列添加定时任务.初始接触它的人都觉得好简单 ...

随机推荐

  1. OpenWrt R2020.03.05 去广告 抗污染 加速 UnPnP NAS

    固件版本已经更新,新版本链接:https://www.cnblogs.com/zlAurora/p/12500932.html   容我啰嗦一下,为什么会有这个固件.   从KoolShare LED ...

  2. 带你看看Java的锁(二)-Semaphore

    前言 简介 Semaphore 中文称信号量,它和ReentrantLock 有所区别,ReentrantLock是排他的,也就是只能允许一个线程拥有资源,Semaphore是共享的,它允许多个线程同 ...

  3. PHP导出excel文件,第一步先实现PHP模板导出不带数据

    今天继续研究PHP导出excel文件,把复杂的事情简单化,一步步实现功能,首先实现模板文件的导出,随后再实现写入数据后导出,最终实现功能,这是基本思路.中间可以加一步,先自己写入数据导出试试,随后再数 ...

  4. 风扇转速通过FPGA采样

    1.风扇最大转速16000RPM,那么每一转需要时间60S/16000=0.00375S=375*10^4ns=T=T1+T2+T3+T4: 2.采样0.6S内的风扇detect信号的上升沿个数:0. ...

  5. 画结构图的神器 Graphviz

    经常看到别人的论文里各种优美的图,没想过它们是怎么来的,前两天也是在看论文的时候被推荐了一款画图软件graphviz,稍微了解了一下这个画图软件,据说是贝尔实验室的几位牛人开发出来的,试了一下觉得很不 ...

  6. 关于oracle怎么看清楚字段的一些实践

    在oracle存储过程或者平时编码中会有很多时候对不上字段,这时候在字段逗号后面可以主动加上--数字. 还有的是应该注意尽量让每个字段都占有一行的空间.下面部分截图说明

  7. Oracle SQLPlus导出数据到csv文件

    时不时地我们需要导出一些数据用作备份.查看报表等,如果用Sql Developer导出会非常慢.而用SqlPlus,则速度非常快. 准备SQL执行文件export.sql: set colsep , ...

  8. hadoop与spark的处理技巧(四)推荐引擎处理技巧

    经常一起购买的商品 scala> var file=sc.textFile("/user/ghj/togeterBought") file: org.apache.spark ...

  9. CSS类与选择器【转】http://www.cnblogs.com/duanhuajian/archive/2012/12/17/2821524.html

    1.在 HTML 中,一个 class 值中可能包含一个词列表,各个词之间用空格分隔.例如,如果希望将一个特定的元素同时标记为重要(important)和警告(warning),就可以写作(这两个词的 ...

  10. 基于elementUI使用v-model实现经纬度输入的vue组件

    绑定一个 [12.34,-45.67] (东经西经,南纬北纬 正负表示) 形式的经纬度数组,能够按度分秒进行编辑,效果如下所示,点击东经,北纬可切换. 经纬度的 度转度分秒 能够获取度分秒格式数据 C ...