JDK 8较不为人所知的一项新 功能是在编译的Java类中包含参数元数据的可选功能[JDK增强建议( JEP ) 118 ]。 此功能允许Java应用程序在运行时通过反射访问此参数元数据信息。
Java Tutorial的Reflection API路径包括一个名为“ 获取方法参数的名称”的课程,该课程讨论并演示了如何在Java 8中应用此新功能。该课程包括一个示例Java类MethodParameterSpy ,可以针对提供的Java类运行该类以指示特征方法和构造函数参数。 本课还强调这是一项可选功能,因为在.class文件中存储其他参数元数据会增加这些文件的大小。 该课程还指出,在某些情况下,参数名称包含开发人员不希望在已编译的.class文件中使用的敏感信息。
通过将-parameters选项传递给javac编译器,可以将其他参数元数据包含在用Java 8编译的.class文件中。 当一个人键入javac -help时,也会显示此-parameters选项,如下一个屏幕快照所示。
javac上的Oracle TechNotes页面指示如何在运行时访问此附加的方法/构造函数参数数据:“将构造函数和方法的形式参数名称存储在生成的类文件中,以便从中获取方法java.lang.reflect.Executable.getParameters Reflection API可以检索它们。” 以下代码片段(名为ParameterDisplayer类)对此进行了演示(重点在displayParametersMetadata(String[])方法上)。
ParameterDisplayer.java
package dustin.examples.jdk8;
import static java.lang.System.out;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
/**
* Uses JDK 8 Parameter class to demonstrate metadata related to the parameters
* of the methods and constructors of the provided class (includes private,
* protected, and public methods, but does not include methods inherited from
* parent classes; those classes should be individually submitted).
*
* @author Dustin
*/
public class ParameterDisplayer
{
private static void displayParametersMetadata(final String[] classesNames)
{
for (final String className : classesNames)
{
try
{
final Class clazz = Class.forName(className);
// Get all class's declared methods (does not get inherited methods)
final Method[] declaredMethods = clazz.getDeclaredMethods();
for (final Method method : declaredMethods)
{
writeHeader(
"Method " + method.toGenericString()
+ " has " + method.getParameterCount() + " Parameters:");
int parameterCount = 0;
final Parameter[] parameters = method.getParameters();
for (final Parameter parameter : parameters)
{
out.println(
"\targ" + parameterCount++ + ": "
+ (parameter.isNamePresent() ? parameter.getName() : "Parameter Name not provided,")
+ (isParameterFinal(parameter) ? " IS " : " is NOT ")
+ "final, type " + parameter.getType().getCanonicalName()
+ ", and parameterized type of " + parameter.getParameterizedType()
+ " and " + (parameter.isVarArgs() ? "IS " : "is NOT ")
+ "variable." );
}
}
}
catch (ClassNotFoundException cnfEx)
{
out.println("Unable to find class " + className);
}
}
}
private static void writeHeader(final String headerText)
{
out.println("\n==========================================================");
out.println("= " + headerText);
out.println("==========================================================");
}
/**
* Indicate whether provided Parameter is final.
*
* @param parameter Parameter to be tested for 'final' modifier.
* @return {@code true} if provided Parameter is 'final'.
*/
private static boolean isParameterFinal(final Parameter parameter)
{
return Modifier.isFinal(parameter.getModifiers());
}
public static void main(final String[] arguments)
{
if (arguments.length < 1)
{
out.println("You must provide the fully qualified name of at least one class.");
System.exit(-1);
}
displayParametersMetadata(arguments);
}
}
我最初考虑过对JDK的知名类运行此类,但是意识到这并不太有用,因为这些类不太可能是使用-parameters选项构建的。 因此,我创建了一个简单的示例类来辅助演示。 它称为ManyMethods然后显示。
ManyMethods.java
package dustin.examples.jdk8;
import java.util.List;
/**
* Class with numerous methods intended to be used in demonstrating JDK 8's new
* Parameter class.
*
* @author Dustin
*/
public class ManyMethods
{
public ManyMethods() {}
private void addArrayOfStrings(String[] strings) {}
private void addManyStrings(final String ... strings) {}
private void addListOfStrings(final List<String> strings) {}
@Override
public String toString()
{
return "ManyMethods";
}
}
接下来的两个屏幕快照演示了ManyMethods不使用-parameters选项的情况下编译的ManyMethods实例上运行ParameterDisplayer 。 最显着的区别是,不使用-parameters选项进行编译时,不提供参数名称。 同样,如果没有-parameters选项,则在编译时参数是否为final也没有可信信息。 在不使用-parameters进行编译的情况下,无论-parameters是否为final , Parameter.getModifiers()方法均不包含final 。
ParameterDisplayer类使用Parameter.isNamePresent()以编程方式标识不存在参数名称(当未使用-parameters选项进行编译时)。 如果未进行检查,则Parameter.getName()返回的参数名称将为“ arg”加上参数编号(第一个参数为arg0,第二个参数为arg1,依此类推)。
ManyMethods类中具有参数的三个方法中的ManyMethods具有该参数的final修饰符。 仅当使用-parameters选项编译类时,才可以通过使用Parameter.getModifiers()进行反射来正确识别这些情况。
略相关的旁注:Sun / Oracle工具文档始终由“ windows”页面和“ solaris”页面组成,后者通常用于描述特定工具如何在Linux和Unix上的所有版本上工作。 我注意到Java 8文档对此进行了更改。 该文档仍然具有“ windows”版本,但是Unix / Linux版本现在其URL中具有“ unix”。 为了说明这一点,这里是Java SE 7和Java SE 8 javac工具页面的URL:
- http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javac.html
- http://docs.oracle.com/javase/8/docs/technotes/tools/unix/javac.html
- http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html
- http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/javac.html
回到新的(使用Java 8) 参数类,值得注意的是,存储此附加参数元数据的已编译.class文件有所增加。 对于上面显示的我的ManyMethods类, .class文件从909字节扩大到961字节。
像Method一样, 构造 方法扩展了Executable ,因此Constructor类享有与Method相同的getParameters方法。 当使用这些额外信息显式编译代码时,Java 8将提供有关方法和构造函数参数的更多详细信息。