Одна из наименее объявленных новых функций JDK 8 — это дополнительная возможность включать метаданные параметров в скомпилированные классы Java [JDK Enhancement Proposal ( JEP ) 118 ]. Эта функция позволяет приложениям Java получать доступ к информации метаданных этого параметра во время выполнения посредством отражения .
След API Reflection API Учебников Java включает урок под названием « Получение имен параметров метода», в котором обсуждается и демонстрируется, как применять эту новую функцию в Java 8. Урок включает в себя пример Java-класса MethodParameterSpy, который можно запускать с предоставленным Java-классом для указания характеристик. метода и параметров конструктора. В этом уроке также подчеркивается, что это необязательная функция, поскольку хранение метаданных дополнительных параметров в файлах .class увеличивает размер этих файлов. Урок также указывает на то, что в некоторых случаях имена параметров содержат конфиденциальную информацию, которую разработчик не хочет, чтобы она была доступна в скомпилированных файлах .class .
Метаданные дополнительных параметров могут быть включены в .class скомпилированные в Java 8, передав опцию -parameters компилятору javac . Эта опция -parameters также отображается, когда один из типов javac -help как показано на следующем снимке экрана.
На странице Oracle TechNotes в javac показано, как можно получить доступ к этим дополнительным данным параметров метода / конструктора во время выполнения: «Сохраняет формальные имена параметров конструкторов и методов в созданном файле класса, чтобы метод java.lang.reflect.Executable.getParameters из Reflection API может их получить ». Следующий фрагмент кода (класс с именем ParameterDisplayer ) демонстрирует это (акцент делается на метод displayParametersMetadata(String[]) ).
ParameterDisplayer.java
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
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
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
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"; }} |
На следующих двух снимках экрана показано, как запустить ParameterDisplayer экземпляров ManyMethods скомпилированных без и с параметром ManyMethods . Наиболее заметные различия заключаются в том, что имена параметров не предоставляются при компиляции без параметра -parameters . Кроме того, нет достоверной информации о том, является ли параметр final при компиляции без параметра -parameters . Метод Parameter.getModifiers () не включает final при компиляции без -parameters независимо от того, является ли параметр фактически final .
Класс ParameterDisplayer использует Parameter.isNamePresent () для программной идентификации того, что имя параметра отсутствует (если оно не скомпилировано с параметром -parameters ). Если бы эта проверка не была сделана, имя параметра, возвращаемое Parameter.getName (), было бы «arg» плюс номер параметра (arg0 для первого параметра, arg1 для второго параметра и т. Д.).
Два из трех методов в классе ManyMethods , у которых был параметр, имели модификатор final для этого параметра. Эти случаи были правильно определены отражением с помощью Parameter.getModifiers () только тогда, когда класс был скомпилирован с параметром -parameters .
Слегка связанная сторона Примечание: документация по инструментам Sun / Oracle всегда состояла из страницы «windows» и страницы «solaris», причем последняя обычно используется для описания того, как конкретный инструмент работает во всех разновидностях в Linux и Unix. Я отметил, что это изменилось в документации по Java 8. Эта документация все еще имеет версию «windows», но версия Unix / Linux теперь имеет «unix» в своем URL. Чтобы проиллюстрировать это, вот URL-адреса для страниц инструментов Java SE 7 и Java SE 8 javac :
- 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) классу Parameter , стоит отметить, что наблюдается увеличение количества скомпилированных файлов .class , в которых хранятся метаданные этого дополнительного параметра. Для моего класса ManyMethods показанного выше, файл .class был увеличен с 909 байт до 961 байт.
Конструктор , как и метод , расширяет исполняемый файл , поэтому класс Constructor тот же метод getParameters, что и Method . Java 8 предоставляет более подробную информацию о параметрах метода и конструктора, когда код явно скомпилирован с этой дополнительной информацией.
| Ссылка: | Метаданные параметров конструктора / метода доступны через отражение в JDK 8 от нашего партнера JCG Дастина Маркса в блоге Inspired by Actual Events . |


