Java 8 能够使用反射 API 获取方法参数名称。
如何获取这些方法参数名称?
据我所知,类文件不存储形式参数名称。如何使用反射获得这些?
Java 8 能够使用反射 API 获取方法参数名称。
如何获取这些方法参数名称?
据我所知,类文件不存储形式参数名称。如何使用反射获得这些?
如何获取这些方法参数名称?
基本上,您需要:
Class
Class
的引用来获取对 a的引用Method
getDeclaredMethod()
getDeclaredMethods()
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 文件不存储形式参数。那么我怎样才能让他们使用反射呢?
请参阅 javadoc Parameter.getName()
:
...如果参数的名称存在,则此方法返回类文件提供的名称。否则,此方法会合成 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
, ...
通过可以附加到各种类文件结构的可选属性来支持这些可选特性是可能的。见4.6。method_info
类文件中结构的方法。另见4.7.1。在 JVM 规范中定义和命名新属性。
由于使用 JDK 8,类文件版本将增加到 52,因此也可以更改文件格式本身以支持此功能。
另请参阅JEP 118:在运行时访问参数名称以获取更多信息和实现替代方案。建议的实现模型是添加一个存储参数名称的可选属性。由于类文件格式已经支持这些可选属性,因此甚至可以通过某种方式使类文件仍然可以被较旧的 JVM 使用,在这种情况下,它们会按照规范的要求被简单地忽略:
Java 虚拟机实现需要默默地忽略它们无法识别的属性。
更新
正如@assylias 所建议的,需要使用javac
命令行选项编译源代码-parameters
,以便将参数名称反射的元数据添加到类文件中。但是,这当然只会影响使用此选项编译的代码 - 上面的代码仍将 printarg0
等,arg1
因为运行时库不是使用此标志编译的,因此在类文件中不包含必要的条目。
谢谢 Andreas,但最后我从 oracle Tutorials on Method Parameters得到了完整的解决方案
它说,
您可以使用方法 java.lang.reflect.Executable.getParameters 获取任何方法或构造函数的形式参数的名称。(Method 和 Constructor 类扩展了 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
您可以使用 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>
根据在 intellij 13 中存储有关方法参数的信息(可通过反射使用), Eclipse IDE 中的“javac -parameters”等价物是“存储有关方法参数的信息(可通过反射使用)”在 Window -> Preferences -> Java - > 编译器。