MethodHandle简单使用

MethodHandle简单使用


  invokevirtual指令用于调用对象的实例方法,根据对象的实际类型进行虚方法分派,这也是Java语言中最常见的方法分派方式。
  invokeinterface指令用于调用接口方法,它会在运行时搜索一个实现了这个接口方法的对象,找出适合的方法进行调用。
  invokespecial指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法。
  invokestatic指令用于调用类方法(static方法)。

 介绍这个主要是因为MethodHandlers.Lookup类中的一些方法和这些指令有联系。

 关于以上这些指令在代码中具体的出现请见下面的例子:

interface SampleInterface {    
    void sampleMethodInInterface();    
}    
   
class S implements SampleInterface {    
    public void sampleMethodInInterface() {}    
    public void normalMethod() {}    
    public void fatherMethod() {}    
    public static void staticSampleMethod() {}    
}  
   
class Sample extends S {    
    public void sampleMethodInInterface() {}    
    public void normalMethod() {}    
    public static void staticSampleMethod() {}    
}    
  
class MethodInvokeTypes {    
    public void invoke() {    
        S sample = new Sample();          ---> 4: invokespecial #3    // Method Sample."<init>":()V  
        sample.sampleMethodInInterface(); ---> 9: invokevirtual #4    // Method S.sampleMethodInInterface:()V  
        Sample newSample = new Sample();  --->16: invokespecial #3    // Method Sample."<init>":()V  
        newSample.normalMethod();         --->21: invokevirtual #5    // Method Sample.normalMethod:()V  
        Sample.staticSampleMethod();      --->24: invokestatic  #6    // Method Sample.staticSampleMethod:()V  
        newSample.fatherMethod();         --->28: invokevirtual #7    // Method Sample.fatherMethod:()V  
    }    
}


方法调用的流程:

 1)名称:要调用的方法的名称一般是由开发人员在源代码中指定的符号名称。这个名称同样会出现在编译之后的字节代码中。

 2)链接:链接包含了要调用方法的类。这一步有可能会涉及类的加载。

 3)选择:选择要调用的方法。在类中根据方法名称和参数选择要调用的方法。

 4)适配:调用者和接收者对调用的方式达成一致,即对方法的类型声明达成共识。

MethodHandle实例:

String a="abcd";
MethodType mt=MethodType.methodType(String.class,int.class,int.class);
MethodHandle handle=MethodHandles.lookup().findVirtual(String.class,"substring",mt);
System.out.println(handle.invoke(a,1,2)); //输出b

 除了invoke方法可以调用外,要注意invokeExact()这个要求严格匹配的方法: 以上改成:handle.invokeExact(a,1,2) 是错误的 一定要写成(String)handle.invokeExact(a,1,2)

MethodType的一些使用:

MethodType mt=MethodType.methodType(void.class,int.class,double.class);
System.out.println(mt);
System.out.println(mt.wrap());
System.out.println(mt.unwrap());
System.out.println(mt.generic());
System.out.println(mt.toMethodDescriptorString());
System.out.println(mt.erase());
输出:
    (int,double)void
    (Integer,Double)Void
    (int,double)void
    (Object,Object)Object
    (ID)V

句柄获取:

 获得方法句柄要通过java.lang.invoke.MethodHandles.Lookup类来完成:

  findConstructor就是查找构造器的

  findVirtual就是查找一般函数的(同invokeVirtual)

  findStatic就是查找静态方法的(同invokeStatic)

  findSpecial查找私有方法的

  findGetter/fingStaticGetter获取属性

findConstructor演示(内部类):

(这是一个错误的例子):  
public class TestMH {  
    
     class Person{  
        private String name;  
        private int age;  
        public String getName() {  
            return name;  
        }  
        public void setName(String name) {  
            this.name = name;  
        }  
        public int getAge() {  
            return age;  
        }  
        public void setAge(int age) {  
            this.age = age;  
        }  
    }  
      
    public static void main(String[] args) throws Throwable {  
        MethodHandles.Lookup lookup=MethodHandles.lookup();  
        MethodHandle mh=lookup.findConstructor(Person.class, MethodType.methodType(void.class));  
        Person p=(Person)mh.invokeExact();  
        System.out.println(p);  
    }  
 } 

 这样的代码会提示:Exception in thread "main" java.lang.NoSuchMethodException: no such constructor: com.cc.dynamic.TestMH$Person.<init>()void/newInvokeSpecial

原因很简单..内部类默认会把外部类的实例传入构造方法,那么自然就不会有参数为空的构造方法了 如下:

// Method descriptor #12 (Lcom/cc/dynamic/TestMH;)V

// Stack: 2, Locals: 2

// TestMH$Person(com.cc.dynamic.TestMH arg0);

在内部类里的方法中都会加入这个参数 如果加个String的参数的构造函数进去:

public TestMH$Person(com.cc.dynamic.TestMH arg0, java.lang.String name);

改正也很简单:

public static void main(String[] args) throws Throwable {  
        MethodHandles.Lookup lookup=MethodHandles.lookup();  
        MethodHandle mh=lookup.findConstructor(Person.class, MethodType.methodType(void.class,TestMH.class));  
        Person p=(Person)mh.invokeExact(new TestMH());  
        System.out.println(p);  
} 

findSpecial演示:

public class TestMH {  
    
    private String privateInfo(){  
        return "10";  
    }  
      
    public static void main(String[] args) throws Throwable {  
        MethodHandle mh=MethodHandles.lookup().findSpecial(TestMH.class, "privateInfo", MethodType.methodType(String.class),TestMH.class);  
        System.out.println(mh.invoke(new TestMH()));  
    }  
}

 如果调用其它类里的私有方法,则会出现错误 

 把参数写成:

    //Person中的私有方法  
    MethodHandle mh=MethodHandles.lookup().findSpecial(Person.class, "privateInfo", MethodType.methodType(String.class),Person.class);  
 或者:
    //Person中的私有方法  
    MethodHandle mh=MethodHandles.lookup().findSpecial(Person.class, "privateInfo", MethodType.methodType(String.class),TestMH.class);  

 都会出错

折衷的方式可以通过反射:

Method m=Person.class.getDeclaredMethod("privateInfo");  
m.setAccessible(true); //破坏掉  
MethodHandle mh=MethodHandles.lookup().unreflect(m);  
System.out.println(mh.invoke(new Person())); 

findGetter演示:

MethodHandles.Lookup lookUp=MethodHandles.lookup();  
MethodHandle mh=lookUp.findGetter(Person.class, "name",String.class);  
System.out.println(mh.invoke(new Person()));  

 Person的name是public的 private会报错(没有访问权限) 这种方式获取的域是可以写成 t.x的形式域

通过其他方式生成MethodHandle:

public static void main(String[] args) throws Throwable {  
    MethodHandle mh=MethodHandles.constant(String.class, "hello");  
    System.out.println((String)mh.invokeExact());  
}  //这种方式生成一个返回指定类型的内容的方法句柄  
  
  MethodHandle mh=MethodHandles.identity(String.class);  
  System.out.println((String)mh.invokeExact("hello"));  //这种方式生成一种输入什么返回什么的方法句柄 

函数柯里化的实现:

public static MethodHandle curry(int number,MethodHandle mh){  
   return MethodHandles.insertArguments(mh, 0, number);  
}  
public static int add(int a,int b){  
   return a+b;  
}  
public static void main(String[] args) throws Throwable {  
   MethodType mt=MethodType.methodType(int.class,int.class,int.class);  
   MethodHandle mh=MethodHandles.lookup().findStatic(TestMH.class,"add",mt);  
   mh=curry(10,mh); //让int add(int,int) 变为int add(5,int)  
   System.out.println(mh.invoke(10));  
}  

MethodHandles中一些其他的方法:

 dropArguments:用来忽略传递的参数的: 第一个参数是MethodHandle对象  第二个参数是忽略参数的起始位置,接下去的参数是忽略参数的类型(和传入的要匹配)

public void testDrop() throws Throwable{  
	MethodHandle cat = MethodHandles.lookup().findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class));  
    assertEquals("xy", (String) cat.invokeExact("x", "y"));  
    MethodHandle d0 = dropArguments(cat, 0, String.class);  
    assertEquals("yz", (String) d0.invokeExact("x", "y", "z"));  
    MethodHandle d1 = dropArguments(cat, 1, String.class);  
    assertEquals("xz", (String) d1.invokeExact("x", "y", "z"));  
    MethodHandle d2 = dropArguments(cat, 2, String.class);  
    assertEquals("xy", (String) d2.invokeExact("x", "y", "z"));  
    MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);  
    assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));  
}

filterArguments:过滤参数的 与其说是过滤其实是对参数进行一下预处理

@Test  
public void testFilter() throws Throwable{  
	MethodHandle cat = lookup().findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class));  
	MethodHandle upcase = lookup().findVirtual(String.class, "toUpperCase", MethodType.methodType(String.class));  
	assertEquals("xy", (String) cat.invokeExact("x", "y"));  
	MethodHandle f0 = filterArguments(cat, 0, upcase);  
	assertEquals("Xy", (String) f0.invokeExact("x", "y")); // Xy  
	MethodHandle f1 = filterArguments(cat, 1, upcase);  
	assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY  
	MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);  
	assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY  
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值