Якщо у мене такий клас:
public class Whatever
{
public void aMethod(int aParam);
}
чи є спосіб дізнатися, що aMethod
використовується параметр з назвою aParam
, тобто тип int
?
Якщо у мене такий клас:
public class Whatever
{
public void aMethod(int aParam);
}
чи є спосіб дізнатися, що aMethod
використовується параметр з назвою aParam
, тобто тип int
?
Відповіді:
Узагальнити:
method.getParameterTypes()
Для написання функцій автозаповнення для редактора (як ви сказали в одному з коментарів) є кілька варіантів:
arg0
, arg1
, і arg2
т.д.intParam
, stringParam
, objectTypeParam
і т.д.У Java 8 ви можете зробити наступне:
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
public final class Methods {
public static List<String> getParameterNames(Method method) {
Parameter[] parameters = method.getParameters();
List<String> parameterNames = new ArrayList<>();
for (Parameter parameter : parameters) {
if(!parameter.isNamePresent()) {
throw new IllegalArgumentException("Parameter names are not present!");
}
String parameterName = parameter.getName();
parameterNames.add(parameterName);
}
return parameterNames;
}
private Methods(){}
}
Тож для вашого класу Whatever
ми можемо зробити тест вручну:
import java.lang.reflect.Method;
public class ManualTest {
public static void main(String[] args) {
Method[] declaredMethods = Whatever.class.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.getName().equals("aMethod")) {
System.out.println(Methods.getParameterNames(declaredMethod));
break;
}
}
}
}
який повинен надрукувати, [aParam]
якщо ви передали -parameters
аргумент своєму компілятору Java 8.
Для користувачів Maven:
<properties>
<!-- PLUGIN VERSIONS -->
<maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
<!-- OTHER PROPERTIES -->
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<!-- Original answer -->
<compilerArgument>-parameters</compilerArgument>
<!-- Or, if you use the plugin version >= 3.6.2 -->
<parameters>true</parameters>
<testCompilerArgument>-parameters</testCompilerArgument>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
Для отримання додаткової інформації див наступні посилання:
Для вирішення цієї ж проблеми була створена бібліотека Паранамера.
Він намагається визначити назви методів кількома різними способами. Якщо клас був складений з налагодженням, він може витягнути інформацію, прочитавши байт-код класу.
Інший спосіб полягає в тому, щоб він вводив приватний статичний член у байт-код класу після його складання, але перед тим, як його розмістити в банку. Потім він використовує рефлексію для вилучення цієї інформації з класу під час виконання.
https://github.com/paul-hammant/paranamer
У мене були проблеми з використанням цієї бібліотеки, але я зрештою змусив її працювати. Я сподіваюся повідомити про проблеми технічному обслуговувачу.
ParameterNAmesNotFoundException
див. клас org.springframework.core.DefaultParameterNameDiscoverer
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));
Так.
Код повинен бути зібраний з компілятором, сумісним з Java 8, з можливістю зберігати ввімкнені формальні імена параметрів ( параметр -параметри ).
Тоді цей фрагмент коду повинен працювати:
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());
}
}
Ви можете отримати метод за допомогою відображення та виявити його типи аргументів. Перевірте http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html#getParameterTypes%28%29
Однак назвати використаний аргумент не можна.
Це можливо, і Spring MVC 3 це робить, але я не знайшов часу, щоб зрозуміти, як саме.
Зіставлення імен параметрів методу до імен змінних шаблонів URI можна здійснити лише в тому випадку, якщо ваш код компільований з увімкненою налагодженням. Якщо ви не ввімкнули налагодження, потрібно вказати ім'я змінної шаблону URI в анотації @PathVariable, щоб прив’язати розв'язане значення імені змінної до параметра методу. Наприклад:
Взяте з весняної документації
Хоча це неможливо (як це показали інші), ви можете використовувати анотацію для перенесення назви параметра та отримати це, хоча і відображення.
Не найчистіше рішення, але воно робить роботу. Деякі веб-сервіси насправді роблять це для збереження назв параметрів (тобто: розгортання WS із скловолокном).
Дивіться java.beans.ConstructorProperties , це анотація, призначена саме для цього.
Отже, ви повинні вміти робити:
Whatever.declaredMethods
.find { it.name == 'aMethod' }
.parameters
.collect { "$it.type : $it.name" }
Але ви, ймовірно, отримаєте такий список, як:
["int : arg0"]
Я вірю, що це буде виправлено в Groovy 2.5+
Тому наразі відповідь така:
Дивитися також:
Для кожного методу, то щось на зразок:
Whatever.declaredMethods
.findAll { !it.synthetic }
.collect { method ->
println method
method.name + " -> " + method.parameters.collect { "[$it.type : $it.name]" }.join(';')
}
.each {
println it
}
aMethod
. Я хочу отримати його для всіх методів у класі.
antlr
для цього імена параметрів?
Як заявив @Bozho, це можна зробити, якщо під час компіляції буде включена інформація про налагодження. Тут хороша відповідь ...
Як отримати назви параметрів конструкторів об'єкта (відображення)? автор @AdamPaynter
... за допомогою бібліотеки ASM. Я зібрав приклад, який показує, як можна досягти своєї мети.
Перш за все, почніть з pom.xml з цими залежностями.
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
<version>5.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
Тоді цей клас повинен робити те, що ти хочеш. Просто виклик статичного методу getParameterNames()
.
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
public class ArgumentReflection {
/**
* Returns a list containing one parameter name for each argument accepted
* by the given constructor. If the class was compiled with debugging
* symbols, the parameter names will match those provided in the Java source
* code. Otherwise, a generic "arg" parameter name is generated ("arg0" for
* the first argument, "arg1" for the second...).
*
* This method relies on the constructor's class loader to locate the
* bytecode resource that defined its class.
*
* @param theMethod
* @return
* @throws IOException
*/
public static List<String> getParameterNames(Method theMethod) throws IOException {
Class<?> declaringClass = theMethod.getDeclaringClass();
ClassLoader declaringClassLoader = declaringClass.getClassLoader();
Type declaringType = Type.getType(declaringClass);
String constructorDescriptor = Type.getMethodDescriptor(theMethod);
String url = declaringType.getInternalName() + ".class";
InputStream classFileInputStream = declaringClassLoader.getResourceAsStream(url);
if (classFileInputStream == null) {
throw new IllegalArgumentException(
"The constructor's class loader cannot find the bytecode that defined the constructor's class (URL: "
+ url + ")");
}
ClassNode classNode;
try {
classNode = new ClassNode();
ClassReader classReader = new ClassReader(classFileInputStream);
classReader.accept(classNode, 0);
} finally {
classFileInputStream.close();
}
@SuppressWarnings("unchecked")
List<MethodNode> methods = classNode.methods;
for (MethodNode method : methods) {
if (method.name.equals(theMethod.getName()) && method.desc.equals(constructorDescriptor)) {
Type[] argumentTypes = Type.getArgumentTypes(method.desc);
List<String> parameterNames = new ArrayList<String>(argumentTypes.length);
@SuppressWarnings("unchecked")
List<LocalVariableNode> localVariables = method.localVariables;
for (int i = 1; i <= argumentTypes.length; i++) {
// The first local variable actually represents the "this"
// object if the method is not static!
parameterNames.add(localVariables.get(i).name);
}
return parameterNames;
}
}
return null;
}
}
Ось приклад з одиничним тестом.
public class ArgumentReflectionTest {
@Test
public void shouldExtractTheNamesOfTheParameters3() throws NoSuchMethodException, SecurityException, IOException {
List<String> parameterNames = ArgumentReflection
.getParameterNames(Clazz.class.getMethod("callMe", String.class, String.class));
assertEquals("firstName", parameterNames.get(0));
assertEquals("lastName", parameterNames.get(1));
assertEquals(2, parameterNames.size());
}
public static final class Clazz {
public void callMe(String firstName, String lastName) {
}
}
}
Ви можете знайти повний приклад на GitHub
static
методами. Тому в цьому випадку кількість аргументів, повернених ASM, різна, але це щось, що можна легко виправити.Імена параметрів корисні лише компілятору. Коли компілятор генерує файл класу, імена параметрів не включаються - список аргументів методу складається лише з кількості та типів його аргументів. Тому неможливо було б отримати ім’я параметра за допомогою відображення (як позначено у вашому запитанні) - воно ніде не існує.
Однак якщо використання відображення не є важкою вимогою, ви можете отримати цю інформацію безпосередньо з вихідного коду (якщо припустити, що у вас є).
Parameter names are only useful to the compiler.
Неправильно. Подивіться на бібліотеку модернізації. Він використовує динамічні інтерфейси для створення REST API-запитів. Однією з його особливостей є можливість визначати імена заповнювачів у шляхах URL та замінювати ці заповнювачі відповідними назвами параметрів.
Щоб додати мої 2 копійки; Інформація про параметр доступна у файлі класу "для налагодження", коли ви використовуєте javac -g для компіляції джерела. І він доступний для APT, але вам знадобиться анотація, тому вам не потрібна. (Хтось обговорював щось подібне 4-5 років тому тут: http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0 )
Загалом коротко кажучи, ви не можете його отримати, якщо безпосередньо не працюєте над вихідними файлами (подібно до того, що APT робить під час компіляції).