Java的动态性支持学习五 方法句柄 概念和句柄类型

Java的动态性支持学习一 - 反射机制 概念概述
Java的动态性支持学习二 - 反射机制 属性、方法、对象的操作
Java的动态性支持学习三 - 反射机制 取消属性、方法、构造器的访问限制
Java的动态性支持学习四 - 反射调用的性能对比
Java的动态性支持学习五 - 方法句柄 概念和句柄类型
Java的动态性支持学习六 - 方法句柄 获取和调用
Java的动态性支持学习七 - 方法句柄调用的性能对比

方法句柄

Java 7增加了一个新的特性,使得Java语言对动态更好的支持,而且性能也有很大的提升,那就是方法句柄。方法句柄是对Java中方法、构造器、字段的一个强类型的可执行的引用。通过方法句柄可以直接调用该句柄所引用的底层方法。从作用上来说,方法句柄的作用类似于反射中的Method类,但方法句柄的功能更强大,使用更灵活,性能更好。

方法句柄的类型

方法句柄是由java.lang.invoke.MethodHandle类表示的。方法句柄的类型(MethodType)完全由它的返回类型和参数类型来确定的,和它所引用的底层方法的名称和所在的类没有关系。比如String类的length方法和Integer类的intValue方法的方法句柄类型是一样的。MethodType类的对象实例只能通过MethodType类中的静态工厂方法创建。这样的工厂方法工有三类。

创建方法句柄的类型

  1. 第一类是通过指定返回值和参数类型来创建MethodType,有多种重载,如下:
    static MethodType	methodType(Class<?> rtype)
    static MethodType	methodType(Class<?> rtype, Class<?> ptype0)
    static MethodType	methodType(Class<?> rtype, Class<?>[] ptypes)
    static MethodType	methodType(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes)
    static MethodType	methodType(Class<?> rtype, List<Class<?>> ptypes)
    static MethodType	methodType(Class<?> rtype, MethodType ptypes)

    第一个参数rtype是返回类型,返回值类型是必须有的,如果返回值是void类型,可以用java.lang.Void.class或void.class来声明。可以有0或多个参数类型。

  2. 第二类是通过genericMethodType方法创建MethodType,有两种重载:
    static MethodType	genericMethodType(int objectArgCount)
    static MethodType	genericMethodType(int objectArgCount, boolean finalArray)
    genericMethodType生成通用的MethodType类型,它们的返回值和所有参数的类型都是Object类。objectArgCount参数的个数,finalArray用来说明是否在参数列表的后面添加一个Object[]类型的参数。
  3. 第三类比较复杂,是通过fromMethodDescriptorString方法创建MethodType类,声明如下:
    static MethodType	fromMethodDescriptorString(String descriptor, ClassLoader loader)
    • 不是很好理解,适合于对Java字节代码格式比较熟悉的开发人员,如String.getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)方法的类型在字节码中表示的形式是"(II[CI)V"。
    • loader是类的加载器,该加载器用来加载方法类型表达式中出现的Java类。

创建MethodType的Demo如下:

//第一类
MethodType mt1 = MethodType.methodType(int.class);  
MethodType mt2 = MethodType.methodType(String.class, String.class);
//第二类
MethodType mt3 = MethodType.genericMethodType(3);  
MethodType mt4 = MethodType.genericMethodType(2, true);  
//第三类
ClassLoader cl = this.getClass().getClassLoader();  
String descriptor = "(Ljava/lang/String;)Ljava/lang/String;";  
MethodType mt5 = MethodType.fromMethodDescriptorString(descriptor, cl);

更改MethodType

MethodType类的所有对象实例都是不可变的,类似于String类,所有对MethodType类对象的修改,都会产生一个新的MethodType类对象。两个MethodType类对象是否相等,只取决于他们所包含的参数类型和返回值类型是否完全一致。改变MethodType的方法如下:

MethodType	appendParameterTypes(Class<?>... ptypesToInsert)
MethodType	appendParameterTypes(List<Class<?>> ptypesToInsert)
MethodType	changeParameterType(int num, Class<?> nptype)
MethodType	changeReturnType(Class<?> nrtype)
MethodType	dropParameterTypes(int start, int end)

Demo如:

//(int,int)String  
MethodType mt = MethodType.methodType(String.class, int.class, int.class);  
//(int,int,float)String  
mtmt = mt.appendParameterTypes(float.class);  
//(int,double,long,int,float)String  
mtmt = mt.insertParameterTypes(1, double.class, long.class);  
//(int,double,int,float)String  
mtmt = mt.dropParameterTypes(2, 3);  
//(int,double,String,float)String  
mtmt = mt.changeParameterType(2, String.class);  
//(int,double,String,float)void  
mtmt = mt.changeReturnType(void.class); 

MethodType还有几个常用的方法:

MethodType	wrap() 把基本类型转换成基本类型的包装类型
MethodType	unwrap() 把基本类型的包装类型转换成基本类型
MethodType	generic() 把返回值和参数类型都变成Object类型
MethodType	erase() 只把引用类型都变成Object类型,基本类型不做处理

Demo如:

//(int,double)Integer  
MethodType mt = MethodType.methodType(Integer.class, int.class, double.class);  
//(Integer,Double)Integer  
MethodType wrapped = mt.wrap();  
//(int,double)int  
MethodType unwrapped = mt.unwrap();  
//(Object,Object)Object  
MethodType generic = mt.generic();  
//(int,double)Object  
MethodType erased = mt.erase();  
原文地址:https://www.cnblogs.com/zhaiqianfeng/p/4618326.html