方法句柄
1. MethodHandle
2. MethodType
3. 使用方法句柄的简单案例
public class MethodHandleTest {
public MethodHandle getHandler() {
MethodHandle mh = null;
MethodType mt = MethodType.methodType(String.class, int.class, int.class);
MethodHandles.Lookup lk = MethodHandles.lookup();
try {
mh = lk.findVirtual(String.class, "substring", mt);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return mh;
}
public static void main(String[] args) throws Throwable {
MethodHandle mh = new MethodHandleTest().getHandler();
String str = "Hello World!";
Object result1 = mh.invoke(str, 1, 3);
Object result2 = (String) mh.invokeExact(str, 1, 3);
/**
* 下面这行代码在运行时会报错,因为方法类型为String.class, int.class, int.class
* 而下面这行代码的返回类型为Object,与申明中为String不符
* 下面这行代码其中第二个参数类型为Integer,与声明中为int不符,则类型适配不符合,系统报错
*/
// Object result3 = mh.invokeExact(str, new Integer(1), 3);
System.out.println("result 1: " + result1);
System.out.println("result 2: " + result2);
}
}
上述代码的输出都是 el
4. 方法句柄的调用过程
- 先通过MethodType的静态工厂方法生成一个包含方法返回类型,方法参数类型的方法类型,也就是MethodType mt = MethodType.methodType(String.class, int.class, int.class)(这里假设调用方法是String类的substring(int, int)方法)
- 然后,获取方法句柄要用到的Lookup对象,例如上述代码中的 lk实例,这个对象可以提供其所在环境中任何可见方法的方法句柄。我们可以把他想象成包含某个类对象的方法成员,方法的容器,通过 lk.findVirtual(String.class, "substring", mt); 具体锁定String类中的名字为"substring",且方法类型为mt的方法,作为方法句柄返回,总而言之,要想从lookup对象中得到方法句柄,需要给出三个参数,分别为,持有所需方法的类,方法的名称,以及跟方法相匹配的方法类型
- 最后,获取到方法句柄之后,我们就可以通过方法句柄来调用底层的方法。方法句柄提供两个方法调用底层方法,invoke()和invokeExact()方法。
5. invoke()方法和invokeExact()方法
- 二者的相同点
- 关于bindTo()方法的代码示例
public class MethodHandleTest {
static class ClassA {
public void println(String s) {
System.out.println(s);
}
}
public static MethodHandle getPrintMH(Object receiver) throws Throwable {
MethodType mt = MethodType.methodType(void.class, String.class);
return lookup().findVirtual(receiver.getClass(), "println", mt).bindTo(receiver);
}
public static void main(String[] args) throws Throwable {
Object obj = System.currentTimeMillis() % 2 == 0 ? System.out : new ClassA();
getPrintMH(obj).invokeExact("HelloWorld!");
}
}
- 程序输出结果总是 HelloWorld
- 二者的不同点
public class MethodHandleTest {
public MethodHandle getHandler() {
MethodHandle mh = null;
MethodType mt = MethodType.methodType(void.class);
MethodHandles.Lookup lk = MethodHandles.lookup();
try {
mh = lk.findVirtual(MethodHandleTest.class, "print", mt);
} catch (Throwable e) {
e.printStackTrace();
}
return mh;
}
public void print() {
System.out.println("print");
}
public static void main(String[] args) throws Throwable {
MethodHandleTest mht = new MethodHandleTest();
MethodHandle mh = mht.getHandler();
int result1 = (int) mh.invoke(mht);
Object result2 = mh.invoke(mht);
System.out.println("result 1:" + result1);
System.out.println("result 2:" + result2);
}
}
程序输出结果为
print
print
result 1:0
result 2:null参考资料:
《深入理解Java虚拟机第三版》
https://www.cnblogs.com/night-wind/p/4405564.html