主题
Java中一个新的反射方式
MethodHandle
这个是 JDK 1.7
引入的。Method Handles
的引入是为了与已经存在的 java.lang.reflect
API
相配合。他们分别是为了解决不同的问题而出现的。
从性能角度上说,MethodHandle
API
要比反射快很多。因为访问检查在创建的时候就已经完成了,而不是像反射一样等到运行时候才检查。但同时,Method Handles
比反射更难用,因为没有列举类中成员,获取属性访问标志之类的机制。
另外,MethodHandles
可以操作方法,更改方法参数的类型和他们的顺序。而反射则没有这些功能。
从以上角度看,反射更通用,但是安全性更差,因为可以在不授权的情况下使用反射对象。而 MethodHandles
遵从了分享者的能力。所以 method handle
是一种更低级的发现,适配和调用方法的方式,唯一的优点就是更快。所以反射更适合主流 Java
开发者,而 method handle
更适用于对编译和运行性能有要求的人。
代码示例
- 获得查找器
MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
- 查找需要的
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%左右。