java的MethodHandle类详解
一.总述
java7为间接调用方法提供了MethodHandle类,即方法句柄。可以将其看作是反射的另一种方式。这是使用MethodHandle调用方法的一个例子:
public class Test {
public static void main(String[] args) throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType mt = MethodType.methodType(String.class,char.class,char.class);
try {
MethodHandle mh = lookup.findVirtual(String.class,"replace", mt);
String handled_str = (String) mh.invoke("abc",'a','c');
System.out.print(handled_str);
} catch (NoSuchMethodException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
用MethodHandle调用方法的流程为:
- (1) 创建MethodType,获取指定方法的签名
- (2) 在Lookup中查找MethodType的方法句柄MethodHandle
- (3) 传入方法参数通过MethodHandle调用方法
二.MethodType
MethodType表示一个方法类型的对象,每个MethodHandle都有一个MethodType实例,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)
如上面示例代码中的
MethodType mt = MethodType.methodType(String.class,char.class,char.class);
就得到了一个方法的参数类型为char,char,返回类型为String的MethodType。
三.Lookup
MethodHandle.Lookup可以通过相应的findxxx方法得到相应的MethodHandle,相当于MethodHandle的工厂方法。查找对象上的工厂方法对应于方法、构造函数和字段的所有主要用例。下面是官方API文档对findxxx的说明,这些工厂方法和结果方法处理的行为之间的对应关系:可以看出findStatic
相当于得到的是一个static方法的句柄,findVirtual
找的是普通方法。其他的可以从官方文档中阅读得知,这里不详细说明了。
四. MethodHandle
MethodHandle是什么?简单的说就是方法句柄,通过这个句柄可以调用相应的方法。官方文档对其的解释为:“ 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.”
翻译如下:
方法句柄是对底层方法、构造函数、字段或类似低级操作的类型化、直接可执行的引用,具有参数或返回值的可选转换。这些转换非常普遍,包括转换、插入、删除和替换等模式
常用的方法为invokexxx,如下图
其中需要注意的是invoke
和invokeExact
,前者在调用的时候可以进行返回值和参数的类型转换工作,而后者是精确匹配的。比如,在MethodType中指定的参数类型是int
,如果你用invoke
调用时指定的参数是Integer
类型,方法调用是可以运行的,这是通过MethodHandle
类的astype
方法产生一个新的方法句柄。而如果用的是invokeExact
则在运行时会报错。
另外一个需要注意的是invokexxx的所有方法返回的是Object
,调用时若有返回结果一般需进行强制类型转换。
最后还有一点,如果调用的方法没有返回值,那么在MethodType
的工厂方法中的返回值类型写为void.class