Java中一个新的反射方式

主题

Java中一个新的反射方式

MethodHandle

这个是 JDK 1.7 引入的。Method Handles 的引入是为了与已经存在的 java.lang.reflect API 相配合。他们分别是为了解决不同的问题而出现的。

从性能角度上说,MethodHandle API 要比反射快很多。因为访问检查在创建的时候就已经完成了,而不是像反射一样等到运行时候才检查。但同时,Method Handles 比反射更难用,因为没有列举类中成员,获取属性访问标志之类的机制。

另外,MethodHandles 可以操作方法,更改方法参数的类型和他们的顺序。而反射则没有这些功能。

从以上角度看,反射更通用,但是安全性更差,因为可以在不授权的情况下使用反射对象。而 MethodHandles 遵从了分享者的能力。所以 method handle 是一种更低级的发现,适配和调用方法的方式,唯一的优点就是更快。所以反射更适合主流 Java 开发者,而 method handle 更适用于对编译和运行性能有要求的人。

代码示例

  1. 获得查找器
// public方法的Lookup
MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
// 所有方法的Lookup
MethodHandles.Lookup lookup = MethodHandles.lookup();
  1. 查找需要的 MethodHandle
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');

代码对比

private static Map<String, Method> METHODS = new ConcurrentHashMap<>();

private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static MethodType TYPE = MethodType.methodType(long.class, DemoBean.class);
private static Map<String, MethodHandle> METHOD_HANDLES = new ConcurrentHashMap<>();

public static long invokeWithReflectCached() {
try {
Method method = METHODS.computeIfAbsent("toLong", key -> {
try {
return DemoInterface.class.getMethod("toLong", DemoBean.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
});
return (Long) method.invoke(new DemoClass(), new DemoBean());
} catch (IllegalAccessException | InvocationTargetException e) {
return -1L;
}
}

public static long invokeWithHandleCached() {
try {
MethodHandle handle = METHOD_HANDLES.computeIfAbsent("toLong", key -> {
try {
return LOOKUP.findVirtual(DemoInterface.class, "toLong", TYPE);
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new RuntimeException(e);
}
});
return (long) handle.invoke(new DemoClass(), new DemoBean());
} catch (Throwable e) {
return -1L;
}
}

在缓存的加持下,MethodHandle比反射性能要好10%左右。