Как получить имена параметров метода в Java 8 с использованием отражения?
Java 8 имеет возможность приобретать имена параметров метода с использованием Reflection API.
-
Как я могу получить эти имена параметров метода?
-
Как известно, файлы классов не хранят формальные имена параметров. Как я могу получить их с помощью отражения?
Ответы
Ответ 1
Как я могу получить эти имена параметров метода (некоторый пример кода)?
В принципе, вам необходимо:
- получить ссылку на
Class
- Из
Class
получить ссылку на 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 это, специфична для реализации (как вы видите, вид выше, сборка 125 JDK 8 не поддерживает его). Формат файла класса поддерживает необязательные атрибуты, которые могут использоваться конкретной реализацией JVM/javac и которые игнорируются другими реализациями, которые его не поддерживают.
Обратите внимание, что вы даже можете генерировать вышеуказанный вывод с помощью arg0
, arg1
,... с JVM до Java 8 - все, что вам нужно знать, - это счетчик параметров, доступный через 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 заключается в том, что для JVM для расширенного API и возможности для предоставления имен реальных параметров вместо arg0
, arg1
,...
Поддержка таких дополнительных функций возможна с помощью дополнительных атрибутов, которые могут быть прикреплены к различным структурам файлов классов. См. 4.6. Методы для структуры method_info
в файле класса. См. Также 4.7.1. Определение и назначение новых атрибутов в спецификации JVM.
Так как с JDK 8 версия файла класса будет увеличена до 52, было бы также возможно изменить формат файла для поддержки этой функции.
См. также JEP 118: Доступ к именам параметров во время выполнения для получения дополнительной информации и альтернатив реализации. Предложенная модель реализации заключается в добавлении необязательного атрибута, в котором хранятся имена параметров. Поскольку формат файла классов уже поддерживает эти необязательные атрибуты, это будет даже возможно, чтобы файлы классов все еще могли использоваться более старыми JVM, где они просто игнорируются по требованию спецификации:
Реализации Java Virtual Machine необходимы для молча игнорирования атрибутов, которые они не распознают.
Обновление
Как было предложено @assylias, источник необходимо скомпилировать с помощью опции javac
командной строки -parameters
, чтобы добавить метаданные для отражения имени параметра в файл класса. Однако это, конечно же, повлияет только на код, скомпилированный с этой опцией - вышеприведенный код будет печатать arg0
, arg1
и т.д., Так как библиотеки времени выполнения не скомпилируются с этим флагом и, следовательно, не содержат необходимых записей в файлы классов.
Ответ 2
Спасибо Андреасу, но, наконец, я получил полное решение из уроков оракула по Параметры метода
В нем говорится:
Вы можете получить имена формальных параметров любого метода или конструктор с методом java.lang.reflect.Executable.getParameters. (Методы классов и Конструктор расширяет класс Executable и, следовательно, наследует метод Executable.getParameters.) Однако файлы .class не сохраняют формальные имена параметров по умолчанию. Это связано с тем, что многие инструменты, которые производить и потреблять файлы классов, может не ожидаться больших статических и динамический размер .class файлов, содержащих имена параметров. В В частности, эти инструменты должны обрабатывать файлы большего размера. виртуальная машина Java (JVM) будет использовать больше памяти. К тому же, некоторые имена параметров, такие как секрет или пароль, могут информация о чувствительных к безопасности методах.
Сохранение формальных имен параметров в определенном файле .class и, следовательно, включить API Reflection для получения формальных имен параметров, компиляции исходный файл с параметром -parameters компилятору javac.
Как скомпилировать
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
Ответ 3
Вы можете использовать Paranamer lib (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>
Ответ 4
в соответствии с Хранить информацию о параметрах метода (можно использовать с помощью отражения) в intellij 13, эквивалент "javac -параметров" в среде Eclipse IDE" Хранить информацию о Параметры метода (можно использовать с помощью отражения) 'в окне → Настройки → Java → Компилятор.