Java对动态类型的支持(转)
转自:https://www.jianshu.com/p/9dd47484e700
1. JDK1.7(JSR-292)对动态类型的直接支持
invokevirtual invokespecial invokestatic invokeinterface的第一个参数都是被调用方法的符号引用(CONSTANT_Methodref_info或者CONSTANT_InterfaceMethodref_info),符号引用在编译期确定了接收者的类型,而动态语言只有在运行期才能确定接收者类型。
对此JDK1.7(JSR-292)提供了invokedynamic指令以及java.lang.invoke包。
2.java.lang.invoke包
主要目的是在之前单纯依靠符号引用来确定调用的目标方法这种方式之外,提供了一种新的动态确定目标方法的机制,称为MethodHandle。
MethodHandler类似于C/C++的函数指针。
之前的Java没有办法单独地把一个函数作为参数进行传递。普遍的做法是设计一个带有方法的接口,以实现了这个接口的对象作为参数。
在拥有了MethodHandle之后,Java拥有了类似于函数指针的工具
public class MethodHandleTest { static class ClassA { public void println(String s) { System.out.println(s); } } public static void main(String[] args) throws Throwable { Object obj = System.currentTimeMillis() % 2 == 0 ? System.out : new ClassA(); getPrintlnMH(obj).invokeExact("hello world"); } private static MethodHandle getPrintlnMH(Object receiver) throws Throwable { MethodType mt = MethodType.methodType(void.class, String.class); return MethodHandles.lookup().findVirtual(receiver.getClass(), "println", mt).bindTo(receiver); } }
MethodType代表方法类型:方法返回值和参数
MethodHandles.lookup():在指定类中查找符合给定方法名称、方法类型并且符合调用权限的方法句柄。
虚方法的第一个参数是隐式的,代表该方法的接收者,也即this指向的对象,该参数之前是放在参数列表中进行传播,现在提供了bindTo()方法。
getPrintlnMH()模拟了invokevirtual指令执行过程,只不过其分派逻辑并非固化在Class文件的字节码上,而是通过一个具体方法来实现。
该方法的返回值MethodHandle对象可以视为对最终调用的一个“引用”。
3.反射机制与MethodHandle机制的区别
- 都是在模拟方法调用,反射是在模拟Java代码层次的方法调用,而MethodHandle模拟字节码层次的方法调用。MethodHandles.lookup中的三个方法——findStatic findVirtual findSpecial正是为了对应于invokestatic invokevirtual&invokeinterface invokespecial这几条字节码指令的执行权限校验行为,而这些底层细节在调用反射API时不需要关心(MethodHandle权限检查在句柄创建阶段完成,在实际调用过程中,JVM并不会检查MethodHandle权限。如果多次被调用,相较于反射调用,会省下重复权限检查的开销)
- java.lang.reflect.Method(重量级)远比java.lang.invoke.MethodHandle(轻量级)所包含的信息多。前者是方法在Java侧的全面映像,包含了方法的签名、描述符以及方法属性表中各种属性,还包含执行权限等运行期信息。 而后者仅仅包含与执行该方法相关的信息。
- MethodHandle可以采用类似于字节码优化技术的思路,反射不行。
- 反射只是为了Java语言服务,MethodHandle是服务于所有JVM上的语言。
作者:王侦
链接:https://www.jianshu.com/p/9dd47484e700
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。