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方法句柄-----5.Method Handles in Java的更多相关文章

  1. java方法句柄-----4.你所不知道的MethodHandle【翻译】

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

  2. java方法句柄-----2.方法句柄的获取、变换、特殊方法句柄

    目录 1.获取方法句柄 1.1查找构造方法.一般方法和静态方法的方法句柄 1.2 查找类中的特殊方法(类中的私有方法) 1.3 查找类中静态域和一般域 1.4 通过反射API得到的Constructo ...

  3. java方法句柄-----1.方法句柄类型、调用

    目录 方法句柄 1.方法句柄的类型 1.1MethodType类的对象实例的创建 1.1.1 通过指定参数和返回值的类型来创建MethodType.[显式地指定返回值和参数的类型] 1.1.2 通过静 ...

  4. java方法句柄-----3.方法句柄的实现接口

    目录 1.使用方法句柄实现接口 1.使用方法句柄实现接口   2.3节介绍的动态代理机制可以在运行时为多个接口动态创建实现类,并拦截通过接口进行的方法调用.方法句柄也具备动态实现一个接口的能力.这是通 ...

  5. Java方法区(Method Area)

    方法区与Java堆一样,是各个线程共享的内存区域,他在与存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据,虽然Java虚拟机规范把方法区描述为堆得一个逻辑部分,但是他却有一个别 ...

  6. Dalvik虚拟机java方法执行流程和Method结构体分析

    Method结构体是啥? 在Dalvik虚拟机内部,每个Java方法都有一个对应的Method结构体,虚拟机根据此结构体获取方法的所有信息. Method结构体是怎样定义的? 此结构体在不同的andr ...

  7. Android C代码回调java方法

    本文将讲述下列三种C代码回调java方法 1.c代码回调java空方法 2.c代码回调java int类型参数方法 3.c代码回调javaString类型参数方法 方法都差不多,先看c代码回调java ...

  8. java方法的虚分派和方法表

    java:方法的虚分派(virtual dispatch)和方法表(method table) Java方法调用的虚分派 虚分配(Virtual Dispatch) 首先从字节码中对方法的调用说起.J ...

  9. Java方法调用机制

    最近在编程时,修改方法传入对象的对象引用,并没有将修改反映到调用方法中.奇怪为什么结果没有变化,原因是遗忘了Java对象引用和内存分配机制.本文介绍3个点: ① 该问题举例说明 ② 简要阐述Java内 ...

随机推荐

  1. 心路历程-安装Docker

    心路历程-安装Docker 本机环境 Windows10 激活HyperV功能 新建CentOS虚拟机 centos docker安装 由于是新的虚拟机,所以没有docker旧版本的问题,不需要卸载旧 ...

  2. Day_09【常用API】扩展案例3_删除源字符串中的指定字符,并计算指定字符出现的次数

    分析以下需求,并用代码实现 1.键盘录入一个源字符串由字符串变量scrStr接收 2.键盘录入一个要删除的字符串由字符串变量delStr接收 3.要求 删除该字scrStr符串中的所有delStr字符 ...

  3. elasticsearch kibana的安装部署与简单使用(二)

    介绍一下elasticsearch和kibana的简单使用 es其实我理解为一个数据库,一个数据库无非就是增删改查, Delete  PUT GET POST 这些接口关键字完美对应 比如,我想查一张 ...

  4. libevent到底是同步还是异步,是阻塞还是非阻塞

    应该从不同角度看libevent的同步/异步.阻塞/非阻塞: IO数量的角度:select出来之前,会阻塞在一个io上,处理完成后再阻塞在下一个io上:之后就是把所有的io fd拿过来,都不阻塞,一个 ...

  5. 题解 洛谷P1562 【还是N皇后】

    原题:洛谷P1562 这个题的原理和8皇后的原理是一模一样的,就是必须要用n个皇后把每一个行填满,同时满足每一列,每一行,每一条对角线只有一个棋子.但如果按照原来的方法暴打的话只有60分(优化亲测无效 ...

  6. httpd+tomcat 均衡负载

    接前面的文章http://www.cnblogs.com/gqdw/p/3785812.html workers.properties worker.list=controller#worker1 w ...

  7. 「雕爷学编程」Arduino动手做(36)——WS2812B 4位彩灯模块

    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器和模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里 ...

  8. 适配器模式C++实现

    目录 类适配器 对象适配器 类适配器 #include <iostream> using namespace std; // Target class Target { public: v ...

  9. 12.1面向对象编程的介绍(oop):封装,继承,多态,访问私有属性

    #封装:内部对数据封装.作用:1.保护数据,防止被随意修改:2.使外部的程序不需要关注内部的构造:只需要提供接口给外部进行访问即可.#继承:一个类就相当于一个模板.通过父类,子类的方式实现不同角色的共 ...

  10. PAT-1060 Are They Equal (科学计数法)

    1060. Are They Equal  If a machine can save only 3 significant digits, the float numbers 12300 and 1 ...