方法句柄

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. 方法句柄的调用过程
  1. 先通过MethodType的静态工厂方法生成一个包含方法返回类型,方法参数类型的方法类型,也就是MethodType mt = MethodType.methodType(String.class, int.class, int.class)(这里假设调用方法是String类的substring(int, int)方法)
  2. 然后,获取方法句柄要用到的Lookup对象,例如上述代码中的 lk实例,这个对象可以提供其所在环境中任何可见方法的方法句柄。我们可以把他想象成包含某个类对象的方法成员,方法的容器,通过 lk.findVirtual(String.class, "substring", mt); 具体锁定String类中的名字为"substring",且方法类型为mt的方法,作为方法句柄返回,总而言之,要想从lookup对象中得到方法句柄,需要给出三个参数,分别为,持有所需方法的类,方法的名称,以及跟方法相匹配的方法类型
  3. 最后,获取到方法句柄之后,我们就可以通过方法句柄来调用底层的方法。方法句柄提供两个方法调用底层方法,invoke()和invokeExact()方法。
5. invoke()方法和invokeExact()方法
  1. 二者的相同点
  • 关于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
  1. 二者的不同点
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);
    }
}
10-23 02:26