简介
Java的反射API从1.0就开始提供,虽然反射的性能在逐步提高,而且对现在大部分应用来说,反射的开销都不太会影响性能。但是如果反射使用特别频繁或需要更进一步提高性能,可以使用从Java1.7开始提供的MethodHandle相关的API。
Method Handle
method handle 的API有以下几个主要对象:
-
Lookup 用于实例化各类method handle对象,是使用method handle的入口对象
-
MethodType 用于表示反射方法的参数对象
-
MethodHandle用于实际操作被反射的字段、方法等
下面使用一个简单的Person类来作为例子:
public class Person {
public String job;
private String name;
private String location;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public void print(){
System.out.println("Person job is " + job);
}
}
获得Class对象
获得class对象,之前常用的有Class.forName(),getClass(),这些方法也可以用。methodHandle也提供了一个新方法:
MethodHandles.lookup().findClass(Person.class.getName());
读写对象实例的字段
从上面可以看到Person的Job字段是public的,使用methodHandle反射读写示例如下:
MethodHandle jobSetter = lookup.findSetter(personClass, "job", String.class);
jobSetter.invoke(p, "fun job");
System.out.println("set job...");
System.out.println("p.job=" + p.job);
MethodHandle jobReader = lookup.findGetter(personClass, "job", String.class);
Object readResult = jobReader.invoke(p);
System.out.println("jobReader="+readResult);
可以看到methodHandle的使用相对来说会比较麻烦,对实例的字段的读写被分为getter和setter,都是MethodHandle对象,不是很好区分,并且只能读写public的字段。
实践中,绝大多数Java对象的字段都是private的,上面这段示例实践中并不太实用。
下面是读写private字段的办法,在Java9之前会显得比较繁琐:
System.out.println("p.getName=" + p.getName());
Field nameField8 = Person.class.getDeclaredField("name");
nameField8.setAccessible(true);
MethodHandle privateNameSetter = lookup.unreflectSetter(nameField8);
privateNameSetter.invoke(p, "MyName");
System.out.println("p.getName=" + p.getName());
MethodHandle privateNameGetter = lookup.unreflectGetter(nameField8);
Object readName = privateNameGetter.invoke(p);
System.out.println("readPrivateName="+readName);
可以看到,需要用methodHandle读写private字段,需要先用reflection api先获取到field,setAccessible为true之后才能读写。
Java9之后有对应的methodHandle方法可以用:
System.out.println("p.getLocation=" + p.getLocation());
MethodHandles.Lookup privateLookup = MethodHandles.privateLookupIn(Person.class, lookup);
MethodHandle locationSetter = privateLookup.findSetter(Person.class, "location", String.class);
locationSetter.invoke(p, "MyLocation");
MethodHandle locationGetter = privateLookup.findGetter(Person.class, "location", String.class);
Object locationGetterResult = locationGetter.invoke(p);
System.out.println("locationGetterResult=" + locationGetterResult);
调用对象的方法
调用对象方法也是类似的:
MethodType printMethodType = MethodType.methodType(void.class);
MethodHandle print = lookup.findVirtual(Person.class, "print", printMethodType);
print.invoke(p);
MethodType构建的第一个参数是返回值class,第二个和之后的就是入参的class。
总结
可以看到MethodHandle的API相对来说比较难用,但是因为不会每次调用都进行安全检查,所以性能相对较高。
参考资料
[1]示例工程:java-api-demos/reflection at master · fengdongyi/java-api-demos (github.com)
评论
发表评论