`

MethodHandle简单使用

 
阅读更多

原文来自:fair-jm.iteye.com 转截请注明出处

 

最近看了一些MethodHandle的使用 东西很杂 七拼八凑 有一些自己的理解可能有错误

因为是七拼八凑的 一些来源我已经记不清楚了...以下买标注哪里的主要来自于《深入理解java7》的迷你书 还有小部分是API或者网上其他的文章中的

 

 

http://www.jdon.com/idea/java/invokedynamic.html 写道
invokedynamic字节码则改变了这种方式,JVM允许其在运行时再进行方法的这种绑定检查,这样,你能够拦截一个根本不存在的方法调用,然后将控制流程转移到另外一个方法里,做些其他事情

MethodHandle 允许对一个方法产生引用,如同对象引用一样,这样避免了繁重的反射。

性能更好 创建MethodHandle时就实现方法检测 而不是调用invoke()时

一般Java类都在编译时检查类型,而invokedynamic的调用不是在编译阶段检查,而是在运行时检查。要做到这点,我们需要定义一个bootstrap,这样在不存在的方法和我们实际方法搭起桥梁。

当我们通过InvokeDynamic调用不存在的方法getDate()时, JVM会使用bootstrap方法来获得真正方法的MethodHandle,然后调用它。JVM缓存了MethodHandle 能提高性能,第一次动态调用将在bootstrap方法中搜索。当然,首先得注册告诉JVM我们的bootstrap方法, 这时得用Linkage.registerBootstrapMethod(String methodName) 方法.                                                                 //注:在现在的JDK7中并未有InvokeDynamic这个类....Linkage似乎也被移除了...

 

Java虚拟机规范 (周志明大大等翻译的版本) 写道
invokevirtual指令用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派),这也是Java语言中最常见的方法分派方式。

invokeinterface指令用于调用接口方法,它会在运行时搜索一个实现了这个接口方法的对象,找出适合的方法进行调用。

invokespecial指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法(§2.9)、私有方法和父类方法。

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对象  第二个参数是忽略参数的起始位置,接下去的参数是忽略参数的类型(和传入的要匹配)

(代码来自API中)

 

  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

 

 

 

其实MethodHandle的内容很多

我这边只是罗列了下基础的使用 还有很多的例如SwitchPoint还有功能更强大的invokedynamic等 

作为基础的笔记 放在这里 给有需要的人 如果笔记哪里错了 麻烦帮忙指正一下

 

1
0
分享到:
评论
1 楼 breadviking 2015-06-05  
MethodHandles.lookup()生成的lookup context只能搜索context能访问到的方法。也就是说只能对context"能见"的方法进行搜索。没有Reflection API setAccessible(boolean)对应的方法

相关推荐

    答复: 通过代码简单介绍JDK 7的MethodHandle,并与.NET的委托对比(二)

    标题中的“答复: 通过代码简单介绍JDK 7的MethodHandle,并与.NET的委托对比(二)”表明本文将深入探讨Java中的MethodHandle概念,并将其与.NET平台上的委托进行对比。MethodHandle是JDK 7引入的一个强大特性,它...

    java-handles-kata:Java反射和不安全的替代方法-MethodHandle和VarHandle API-修复代码卡塔中损坏的测试

    以下是一个简单的示例,展示了如何从MethodHandle到VarHandle的过渡: ```java // 使用反射 Method method = SomeClass.class.getMethod("someMethod", argTypes); method.invoke(someInstance, args); // 使用...

    Java 动态代理

    在Java编程中,动态代理是一种强大的技术,它允许我们在运行...无论是使用Proxy还是MethodHandle,都需要根据具体需求来选择合适的方式,实现更加优雅和高效的代码。在实践中不断探索,才能更好地掌握这一强大的工具。

    jclasslib.rar_Java 8_java8 下载_java字节码查看工具;_jclasslib.jar_允许修改Jav

    简单的说:用户可以通过jclasslib修改jar包下面的类,是一个实用的底层修改工具,需要Java环境配置才能使用。有需要的朋友可以下载试试! 软件功能 1、支持Java7-Java8(使用-target 1.7编译的类文件)。 2、...

    自己写的动态代理的小程序 运行一下 动态代理很快理解

    在Java中,动态代理主要有两种实现方式:一是使用`java.lang.reflect.Proxy`类,二是使用Java 8引入的`java.lang.invoke.MethodHandles`和`java.lang.invoke.MethodHandle`类。这里我们主要讨论`Proxy`类的使用。 1...

    JavaEye论坛热点推荐_-_2009年09月_-_总第16期.pdf

    7. **Swing小应用(Todo-List)**:展示了如何使用Swing构建一个简单的待办事项列表应用,提供了基础的GUI编程示例。 8. **日志库性能比较**:比较了log4j与JDK内置的日志系统在性能上的差异,为选择合适的日志库...

    HandleWrapper:动态内联MethodHandles

    肯定有比这简单的解决方案,但是我想学习有关字节码的一两本书,因此在这里我使用在运行时创建包含MethodHandle作为静态final字段的类。 这些“ HandleWrappers”几乎与直接调用一样快。 Benchmark Mode Cnt Score...

    jdk1.7.0_17 绿色解压缩版

    6. **动态类型**:引入了`java.lang.invoke.MethodHandle`和相关类,支持动态方法调用。 **安全性和性能优化** JDK 1.7.0_17作为更新版,通常会包含对安全性和性能的优化。这些更新可能包括漏洞修复、内存管理改进...

    JDK8 版本和demo

    例如,可以创建一个简单的lambda表达式示例,演示如何使用流处理集合,或者利用新的日期和时间API来处理日期相关的问题。通过实际操作,我们可以更好地理解这些新功能在实际开发中的应用。 在学习和使用JDK8时,...

    Java 18 新功能介绍.doc

    JEP 416优化了Java的反射机制,通过使用方法句柄(MethodHandle)来提升性能。方法句柄是Java 7引入的一个低级API,用于直接访问方法和字段,相比传统的反射,它提供了更高的效率和灵活性。 5. **JEP 417: Vector ...

    jdk1.7 windows 64 免安装

    Java Development Kit(JDK)是Java编程语言的...总之,JDK 1.7 Windows 64位免安装版为开发者提供了便捷的开发环境,只需简单的解压和配置环境变量即可开始编写和运行Java程序,同时享受这一版本带来的诸多增强功能。

    动态代理原理实例Demo

    动态代理主要有两种实现方式:JDK自带的`java.lang.reflect.Proxy`类和`java.lang.invoke.MethodHandle`API。这里主要讨论JDK的Proxy类,因为这是最常见且相对简单的实现方式。 1. **Proxy类的使用** - **Proxy....

    jdk1.7.0_79.jdk.zip

    1. **多线程增强**:包括Fork/Join框架,使得并行编程更为简单。 2. **字符串改进**:支持对字符串进行in-place替换和删除操作,减少了对象创建。 3. **try-with-resources语句**:自动关闭资源,避免资源泄露。 4. ...

    jdk11(开源免费版).rar

    JDK11增强了对动态类型语言的支持,通过`java.lang.invoke.MethodHandle`类的改进,使得在运行时更灵活地处理方法调用,这对实现脚本语言绑定和元编程有显著帮助。 4. **字符串和集合的改进** - `String`类增加了...

    jdk api 1.8中文API

    例如,LocalDate、LocalTime、LocalDateTime和ZonedDateTime等类,使日期和时间的操作变得简单而精确。 在并发编程方面,Fork/Join框架和Parallel Streams的结合使得多核处理器的性能得以充分利用。Fork/Join框架是...

    中文版jdk1.8chmAPI文档

    JDK1.8对反射API也做了改进,如MethodHandle和MethodType等,提供了一种更高效、更安全的方式来访问和调用方法。 十、模块系统(Project Jigsaw) 虽然JDK9才正式引入模块系统,但JDK1.8已经开始了这个重要特性的...

    JDK1.8中文版

    2. **Stream API**:Stream API是Java 8的新特性,它提供了一种新的数据处理方式,允许对集合进行声明式操作,如过滤、映射、合并等,使并行处理变得更加简单。Stream API与lambda表达式结合使用,能实现高效的函数...

    JDK 1.8_CHM中文_JDIT.rar

    新添加的`MethodHandle`和`MethodType`类提供了更强大的动态类型操作,而注解处理器的增强则让元数据驱动的编程模式更加灵活。 总的来说,"JDK 1.8_CHM中文_JDIT.CHM"这个文档资源是Java初学者的必备工具,它涵盖了...

    jdk8源码.zip

    9. **新的反射API**:JDK 8提供了更强大的反射API,比如`MethodHandles`和`MethodHandle`,它们提供了更灵活的方法调用机制。 10. ** Nashorn JavaScript引擎**:JDK 8引入了Nashorn JavaScript引擎,允许Java代码...

    JAVA类加载机制与动态代理

    5. **使用JDK 1.7的动态语言支持时**,如果`java.lang.invoke.MethodHandle`实例最后的解析结果是`REF_getStatic`、`REF_putStatic`、`REF_invokeStatic`的方法句柄,并且这个方法句柄对应的类没有初始化,则需要先...

Global site tag (gtag.js) - Google Analytics