什么是MethodHandle?
从Java 7开始提供了另一套API MethodHandle 。其与反射的作用类似,可以在运行时访问类型信息,但是据说其执行效率比反射更高,也被称为Java的 现代化反射。
官方对其定义如下:
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.
如何通过进行反射?
通过方法句柄来访问一下某个类里面的方法以及属性等,总的来说只需要4步:
- 创建Lookup
- 创建MethodType
- 基于Lookup与MethodType获得MethodHandle
- 调用MethodHandle
创建一个反射的目标类用于后面的例子
public class Hello {
private String name;
public void hello(String name){
System.out.println("hello "+name);
}
public void say(String name){
System.out.println("say "+name);
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
-
创建Lookup
MethodHandles是MethodHandle的工厂类,它提供了一系列静态方法用于获取MethodHandle。使用如下代码创建一个lookup,以这种方式得到的lookup很强大,凡是调用类支持的字节码操作,它都支持。
MethodHandles.Lookup lookup = MethodHandles.lookup();
还可以使用publicLookup方法创建,但是以此种方式创建的lookup只能访问类中public的成员。
MethodHandles.Lookup publicLookup=MethodHandles.publicLookup();
-
创建MethodType
第一个参数是方法的返回类型,第二参数开始为方法的形式参数类型(例子中只有一个String类型参数,实际可以有多个)
MethodType.methodType(Void.class, String.class)
-
基于Lookup与MethodType创建MethodHandle
MethodHandle helloMethod = lookup.findVirtual(Hello.class, "hello", MethodType.methodType(Void.class, String.class));
-
通过MethodHandle传入对象实例targetInstance和方法参数字符串“MethodHandle”调用对象方法hello
Hello targetInstance = new Hello(); helloMethodHandle.invoke(helloInstance,"MethodHandle");
-
通过MethodHandle传入对象实例helloInstance和属性名称获取属性值
//访问属性,需要借助反射得到Field,如果改属性是private的,还需要设置Field的setAccessible为true Field nameField = Hello.class.getDeclaredField("name"); nameField.setAccessible(true); MethodHandle nameHandle = lookup.unreflectGetter(nameField); System.out.println((String) nameHandle.invoke(targetInstance));
反射API
创建MethodHandle
主要通过lookup里面的以下方法来创建MethodHandle
创建构造函数MethodHandle
public MethodHandle findConstructor(Class<?> refc, MethodType type)
refc: 要检索的类
type: 对应的构造函数的MethodType
创建实例方法MethodHandle
public MethodHandle findVirtual(Class<?> refc, String name, MethodType type)
name: 方法名称
创建类方法的MethodHandle
public MethodHandle findStatic(Class<?> refc, String name, MethodType type)
创建非private的Field的访问MethodHandle 。注意这个不是获取field的javabean Setter方法,与其毫无关系。通过这个setter 方法句柄我们就可以访问到这个属性了。
public MethodHandle findGetter(Class<?> refc, String name, Class<?> type)
对应的如果要设置此属性的值,使用Setter方法句柄
public MethodHandle findSetter(Class<?> refc, String name, Class<?> type)
通过MethodHandle调用目标方法
public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable;
结语
- Java 9提供了增强的VarHandle,VarHandle主要用于动态操作数组的元素或对象的成员变量。VarHandle与MethodHandle非常类似,它也需要通过MethodHandles来获取实例,接下来调用VarHandle的方法即可动态操作指定数组的元素或指定对象的成员变量。
- 上文可知MethodHandle(方法句柄)访问目标对象属性值的时候需要借助反射的API。回忆一下Java反射一文中的结语,“但如果在Java 9或更高的版本中使用这种方式会看见这样的警告。而Java 16开始默认拒绝非法访问,于是会直接报错”。所以refection和invoke包的这两种方式都不是完全反射,java还提供了一种完全反射的方法,即通过UnSafe,下文详解。