Как получить имена параметров метода в Java 8, используя отражение?
Java 8 имеет возможность получать имена параметров метода, используя Reflection API.
Как я могу получить эти имена параметров метода?
Насколько мне известно, файлы классов не хранят формальные имена параметров. Как я могу получить их, используя отражение?
4 ответа
Как я могу получить эти имена параметров метода (некоторый пример кода)?
В основном вам необходимо:
- получить ссылку на
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 необходимы для игнорирования атрибутов, которые они не распознают.
Обновить
Как предполагает @assylias, источник должен быть скомпилирован с javac
опция командной строки -parameters
чтобы добавить метаданные для отражения имени параметра в файл класса. Однако это, конечно, повлияет только на код, скомпилированный с этой опцией - приведенный выше код будет по-прежнему печататься arg0
, arg1
и т.д., поскольку библиотеки времени выполнения не компилируются с этим флагом и, следовательно, не содержат необходимых записей в файлах классов.
Спасибо Андреас, но, наконец, я получил полное решение из Учебников оракула по параметрам метода
Это говорит,
Вы можете получить имена формальных параметров любого метода или конструктора с помощью метода java.lang.reflect.Executable.getParameters. (Классы Method и Constructor расширяют класс Executable и поэтому наследуют метод Executable.getParameters.) Однако в файлах.class по умолчанию не хранятся имена формальных параметров. Это связано с тем, что многие инструменты, которые создают и используют файлы классов, могут не рассчитывать на большую статическую и динамическую площадь файлов.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
Согласно " Хранить информацию о параметрах метода (которую можно использовать с помощью отражения") в intellij 13, эквивалентом "javac -parameters" в Eclipse IDE является "Хранение информации о параметрах метода (которую можно использовать с помощью отражения)" в "Окне" -> "Настройки" -> Java -. > Компилятор.
Вы можете использовать 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>