Как получить имена параметров метода в Java 8, используя отражение?

Java 8 имеет возможность получать имена параметров метода, используя Reflection API.

  1. Как я могу получить эти имена параметров метода?

  2. Насколько мне известно, файлы классов не хранят формальные имена параметров. Как я могу получить их, используя отражение?

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>
Другие вопросы по тегам