【发布时间】:2014-01-30 11:37:11
【问题描述】:
Java 8 能够使用反射 API 获取方法参数名称。
如何获取这些方法参数名称?
据我所知,类文件不存储形式参数名称。如何使用反射获得这些?
【问题讨论】:
标签: java reflection java-8 method-parameters
Java 8 能够使用反射 API 获取方法参数名称。
如何获取这些方法参数名称?
据我所知,类文件不存储形式参数名称。如何使用反射获得这些?
【问题讨论】:
标签: java reflection java-8 method-parameters
如何获取这些方法参数名称?
基本上,您需要:
Class 的引用
Class,通过调用getDeclaredMethod() 或getDeclaredMethods() 获取对Method 的引用,这将返回对Method 对象的引用Method 对象调用(Java 8 中的新功能)getParameters(),它返回一个Parameter 对象数组Parameter 对象上,调用getName()
Class<String> clz = String.class;
for (Method m : clz.getDeclaredMethods()) {
System.err.println(m.getName());
for (Parameter p : m.getParameters()) {
System.err.println(" " + p.getName());
}
}
输出:
...
indexOf
arg0
indexOf
arg0
arg1
...
据我所知,.class 文件不存储形式参数。那么我怎样才能使用反射来获取它们呢?
查看Parameter.getName()的javadoc:
... 如果参数名称存在,则此方法返回类文件提供的名称。 否则,此方法合成一个argN形式的名称,其中N是声明参数的方法的描述符中参数的索引。
JDK 是否支持这一点,是特定于实现的(从上面的输出中可以看到,JDK 8 的 build 125 不支持它)。类文件格式支持特定 JVM/javac 实现可以使用的可选属性,而其他不支持它的实现会忽略这些属性。
请注意,您甚至可以使用 Java 8 之前的 JVM 使用 arg0、arg1... 生成上述输出 - 您只需要知道可通过 Method.getParameterTypes() 访问的参数计数:
Class<String> clz = String.class;
for (Method m : clz.getDeclaredMethods()) {
System.err.println(m.getName());
int paramCount = m.getParameterTypes().length;
for (int i = 0; i < paramCount; i++) {
System.err.println(" arg" + i);
}
}
JDK 8 的新功能是扩展 API 和 可能性 供 JVM 提供 实际参数名称 而不是 arg0、arg1 , ...
通过可附加到各种类文件结构的可选属性,可以支持此类可选功能。有关类文件中的 method_info 结构,请参见 4.6. Methods。另请参阅 JVM 规范中的 4.7.1. Defining and Naming New Attributes。
从 JDK 8 开始,class 文件版本将增加到 52,也可以更改文件格式本身以支持此功能。
另请参阅JEP 118: Access to Parameter Names at Runtime 了解更多信息和实施替代方案。建议的实现模型是添加一个存储参数名称的可选属性。由于类文件格式已经支持这些可选属性,这甚至可以通过某种方式使类文件仍然可以被较旧的 JVM 使用,在这种情况下,它们会按照规范的要求被简单地忽略:
Java 虚拟机实现需要静默忽略它们无法识别的属性。
更新
正如@assylias 所建议的,需要使用javac 命令行选项-parameters 编译源代码,以便将参数名称反射的元数据添加到类文件中。但是,这当然只会影响使用此选项编译的代码 - 上面的代码仍将打印 arg0、arg1 等,因为运行时库不是使用此标志编译的,因此在类文件。
【讨论】:
-parameter 标志进行编译。
-parameters(以's'结尾)
您可以使用 Paranamer 库 (https://github.com/paul-hammant/paranamer)
适合我的示例代码:
import com.thoughtworks.paranamer.AnnotationParanamer;
import com.thoughtworks.paranamer.BytecodeReadingParanamer;
import com.thoughtworks.paranamer.CachingParanamer;
import com.thoughtworks.paranamer.Paranamer;
Paranamer info = new CachingParanamer(new AnnotationParanamer(new BytecodeReadingParanamer()));
Method method = Foo.class.getMethod(...);
String[] parameterNames = info.lookupParameterNames(method);
如果您使用 Maven,则将此依赖项放在您的 pom.xml 中:
<dependency>
<groupId>com.thoughtworks.paranamer</groupId>
<artifactId>paranamer</artifactId>
<version>2.8</version>
</dependency>
【讨论】:
感谢 Andreas,但最后我从 Method Parameters 上的 oracle 教程中获得了完整的解决方案
它说,
您可以获取任何方法的形参名称或 带有方法的构造函数 java.lang.reflect.Executable.getParameters。 (类方法和 构造函数扩展了 Executable 类,因此继承了 方法 Executable.getParameters。)但是,.class 文件不存储 默认情况下形式参数名称。这是因为许多工具 生产和消费类文件可能不会期望更大的静态和 包含参数名称的 .class 文件的动态足迹。在 特别是,这些工具必须处理更大的 .class 文件,并且 Java 虚拟机 (JVM) 将使用更多内存。此外, 一些参数名称,例如秘密或密码,可能会暴露 有关安全敏感方法的信息。
将形式参数名称存储在特定的 .class 文件中,因此 启用反射 API 以检索形式参数名称,编译 javac 编译器的带有 -parameters 选项的源文件。
Remember to compile the with the -parameters compiler option
java MethodParameterSpy ExampleMethods
此命令打印以下内容:
Number of constructors: 1
Constructor #1
public ExampleMethods()
Number of declared constructors: 1
Declared constructor #1
public ExampleMethods()
Number of methods: 4
Method #1
public boolean ExampleMethods.simpleMethod(java.lang.String,int)
Return type: boolean
Generic return type: boolean
Parameter class: class java.lang.String
Parameter name: stringParam
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Parameter class: int
Parameter name: intParam
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Method #2
public int ExampleMethods.varArgsMethod(java.lang.String...)
Return type: int
Generic return type: int
Parameter class: class [Ljava.lang.String;
Parameter name: manyStrings
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Method #3
public boolean ExampleMethods.methodWithList(java.util.List<java.lang.String>)
Return type: boolean
Generic return type: boolean
Parameter class: interface java.util.List
Parameter name: listParam
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Method #4
public <T> void ExampleMethods.genericMethod(T[],java.util.Collection<T>)
Return type: void
Generic return type: void
Parameter class: class [Ljava.lang.Object;
Parameter name: a
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
Parameter class: interface java.util.Collection
Parameter name: c
Modifiers: 0
Is implicit?: false
Is name present?: true
Is synthetic?: false
【讨论】:
根据Store information about method parameters (usable via reflection) in intellij 13,Eclipse IDE 中“javac -parameters”的等效项是“在 Window -> Preferences -> Java -> Compiler 中存储有关方法参数的信息(可通过反射使用)”。
【讨论】: