Java的反射技术并不陌生,它的一个应用就是获取方法的参数信息。
1、传统方法获取参数类型
比较常用的方法就是通过Method的getParameterTypes()方法:
1 | package hello.reflect; |
来看一下Java中所有类的祖先Object。输出信息:
1 | method name: wait |
可以看出,通过getParameterTypes()方法只能获取方法的参数类型,而无法获取参数名。
2、Java8新功能
在jdk8中一个新特性就是它可以将方法参数的院信息存储到编译玩的class文件中(JEP 118)。利用这个特性就可以用反射技术来获取更多方法的参数信息。
于是将程序改成下面这样:
1 | package hello.reflect; |
输出结果如下:
1 | method name: wait |
可以看出,输出的参数名是从arg0开始一直增加,这显然不是我们想要的结果,比如Object类中的wait方法源码如下
1 | public final void wait(long timeout, int nanos) throws InterruptedException { |
我们想要的是timeout,nanos这两个参数名,显然输出的并不是。
原因就在于输出中的那个false,它是paramater.isNamePresent()方法的返回值,false说明了改class文件中并没有提供方法的参数名。也就是说,想要让class文件中存储方法的参数名称信息,需要在编译的时候显示的声明。
3、javac的parameters选项
显示声明的方式可以在编译命令的说明中找到,打开命令行,输入 javac -help:
上面说的明明白白,需要加入parameters参数才可以。
但是显然Object的类是无法重新编译的,于是是自己新建了一个类:
1 | package hello.reflect; |
我用javac命令编译试了一下,加上parameters参数之后,class文件明显变大了几十k。
4、eclipse中加入parameters编译参数
在eclipse中开发的时候编译默认是不带这个参数的,也就是说用上面的方法输出我新建的ClassA,参数名肯定还是arg0这些,需要额外设置一下才可以。
具体方法就是右键工程,选择properties
把最下面那个选项选上,再重新编译一下,就可以了。
关于重新编译,这个问题也经常遇到,有时候如果少了某个class文件经常会输出找不到或无法加载主类的错误,这时候就需要重新编译了。选择最上面的project-clean,然后再选择要重新编译的工程。之后再次运行,就会重新编译。
这是再运行,将ClassA作为参数就可以得到下面的输出:
1 | method name: toString |
现在就可以获取方法的参数名称了。
5、总结
这个功能看似很无聊,但是在实际开发中应用还是很广的 ,之所以记录下来就是因为最近在使用github上面一个cdp4j的库,下载到本地以后一直运行错误,最后排查原因才知道是这个原因。
这个功能在使用动态代理时用于获取参数信息还是很有用的。