java.lang.invoke 包
在JDK1.7发布之后引入了java.lang.invoke包,它是JSR-292的一个重要组成部分,这个包的主要目的实在之前单纯的依靠符号引用来去定调用的目标方法这种方式以外,提供一种新的动态确定目标方法的机制,成为MethodHandle。在此之前java语言没有办法单独的把一个函数作为参数传递。普遍的做法是设计一个带有某个方法的接口比如(带有compare()方法的Comparator接口),已实现了这个接口的对象作为参数,例如Clooections.sort()就是这样定义的,void sort(List list, Comparator c).
如下代码展示MethodHandle的基本用途,无论obj是何种类型(临时定义的ClassA或者是实现PrintStream接口的实现类System.out),都可以正确的调用到println()方法。
实际上,方法getPrintlnMH()模拟了invokevirtual指令的智行过程,只不过他的分派逻辑并非固话在Class文件的字节码上,而是通过一个具体方法来实现。而这个方法本身的返回值(MethodHandle对象),可以视为对最终调用方法的一个引用。以此为基础,有了MethodHandle对象就可以写出类似于下面这样的函数声明:
void sort(List list, MethodHandle compare)
从上面的例子可以看出,使用MethodHandle并没有什么困难,不过看完他的用法之后,我们可能会有疑问,相同的事情,用反射也可以实现。仅站在java的角度,MethodHandle的使用方法和效果与Reflection有众多相似之处,不过他们也有一些去表:
1、从本质上讲,Reflection和MethodHandle机制都是在模拟方法调用,但是Reflection实在模拟java到吗层次的方法调用,而MethodHandle实在模拟字节码层次的方法调用。在MethodHandles.lookup中的三个方法fundStatic(),findVirtual(),fundSpecial()正式为了对应invokestatic,invokevirtual&invokeinterface和invokespecial这几条字节码指令的执行权限校验行为,而这些底层细节在使用Reflection API时是不需要关心的。
2、Reflection中的java.lang.reflect.Method对象远比MethodHandle机制中的java.lang.invoke.MethodHandle对象所包含的信息多,前者是方法在java一段的全面映射,包含了方法的签名,描述符以及方法属性表中各种属性的java端表示方式,还包含智行权限等的运行期信息,而后者仅仅包含与智行该方法相关的信息。用通俗的话讲,Reflection是重量级,而MethodHandle是轻量级。
3、由于MethodHandle是对字节码的方法指令调用的模拟,所以理论上虚拟机在这方面做得各种优化(如方法内联),在MethodHandle上也应当可以采用类似思路去支持,通过反射去调用方法则不行。