代码中如何获得 Java 方法的形式参数名

对于一个 Java 方法 foo(int id, String name); 我们如何能在代码中获得形式参数名 id 和 name 呢?

我们知道通过反射 API Method.getGenericParameterTypes() 可以获得方法的参数类型,但是对于参数名一般就是 arg0, arg1, arg2 ..., 因为 Java 编译时把形式参数名擦除了。所以对完全擦除了形式参数名的字节码应该是没办法了,但我们自己写的类还是有能力去管控的。

对于自己写的类,有两种办法获得形式参数名,分别是

1) Java8 的 -parameters 编译参数,然后用 Java8 新引入的反射 API Parameter

我们先在 Java8 下运行下面的代码

得到结果是

false
int: arg0
false
String: arg1

默认是也是得不到参数名的,isNamePresent() 是 false

如果编译的时候带上 -parameters 参数,再执行上面的代码结果就是

true
int: id
true
String: name

注:加不加编译参数 -parameters 产生的字节码用 javap -c 去反编译是看不出分别的,但是二进制码确实不一样的。看这里 Obtaining Names of Method Parameters. 现面一个对比,分别是没有加和加了 -parameters 编译出来的结果,直接用 vi 就能看出不同

java8-parameters

上半部分是没有加 -parameters 编译参数产生的字节码,下半部是加了 -parameters 编译参数产生的字节码,注意看 foo() 方法部分,有 -parameters 则保留了形式参数名称在字节码中。

上面的对比结果同时也告诉了我 javap -c 并不是字节码的全部,javap -c 看到的相同也不意味着执行行为是一致的。

2) Play1 中的 play.utils.Java 的方法 public static String[] parameterNames(Method method)

这种方式值得参数一下,它用到了 javaassist  和 bytecodeparser 两个工具包,并依赖于 Play1 的编译方式,Play2 中已不适用。这里只列出它的源代码实现,已注释部分也在

摘自:play.utils.Java

上面有个 play.classloading.enhancers.LVEnhancer, 大致看下用到的两个方法

摘自:play.classloading.enhancers.LVEnhancer

其他:在 Play1 的世界里因为接管了编译器的某些活,所以它似乎还有办法获得实际传入方法的变量名,如在调用方法 foo() 时使用

int someId = 123;
String someName = "http://unmi.cc";
foo(someId, someName)

在代码中可以把 someId 和  someName 这两个名字得到

因为依据 Play1 对模板的调用

Detail orderDetail = ....
renderTemplate("details.html", orderDetail);

到模板文件 details.html 里就能用 ${orderDetail.id} 那样来访问 orderDetail 了,Java 代码中的变量名 orderDetail 和模板中的 orderDetail 必须对应的。


在 Play2 中开始使用 ParaNamer 来得到方法参数名称,见 https://github.com/paul-hammant/paranamer

更有趣的是编译时用 javac -g:vars TestLocalVarNames.java,然后用  javap -l 可以查看到变量名列表 LocalVariableTable

本文链接 https://yanbin.blog/retrieve-java-method-formal-parameter-names/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments