Если у меня есть класс вроде этого:
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>
<compilerArgument>-parameters</compilerArgument>
<testCompilerArgument>-parameters</testCompilerArgument>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
Для получения дополнительной информации см. следующие ссылки:
Для решения этой проблемы была создана библиотека Paranamer.
Он пытается определить имена методов несколькими способами. Если класс был скомпилирован с отладкой, он может извлечь информацию, читая байт-код класса.
Другой способ - вставить частный статический член в байт-код класса после его компиляции, но до того, как он будет помещен в банку. Затем он использует отражение для извлечения этой информации из класса во время выполнения.
https://github.com/paul-hammant/paranamer
У меня возникли проблемы с использованием этой библиотеки, но в конце концов я работал с ней. Я надеюсь сообщить о проблемах сопровождающему.
Вы можете получить метод с отражением и определить его типы аргументов. Проверьте http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html#getParameterTypes%28%29
Однако вы не можете указать имя используемого аргумента.
Да.
Код должен быть скомпилирован с компилятором, совместимым с 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());
}
}
Пока это невозможно (как иллюстрируют другие), вы можете использовать аннотацию для переноса имени параметра и получить это отражение.
Не самое чистое решение, но оно выполняет свою работу. Некоторые веб-службы на самом деле делают это, чтобы сохранить имена параметров (то есть: развертывание WSs с помощью стеклянной рыбы).
Возможно, и Spring MVC 3 делает это, но я не нашел времени, чтобы посмотреть, как именно.
Согласование имен параметров метода для имен переменных шаблона URI можно только если ваш код скомпилирован с включенной отладкой. Если у вас есть не отладка включена, вы должны указать имя шаблона URI имя переменной в @PathVariable аннотации, чтобы связать разрешенное значение имени переменной для параметр метода. Например:
Взято из spring документация
Смотрите 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
}
см. org.springframework.core.DefaultParameterNameDiscoverer класс
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));
Имена параметров полезны только для компилятора. Когда компилятор создает файл класса, имена параметров не включаются - список аргументов метода состоит только из числа и типов его аргументов. Таким образом, было бы невозможно получить имя параметра с помощью отражения (как отмечено в вашем вопросе) - он нигде не существует.
Однако, если использование отражения не является жестким требованием, вы можете получить эту информацию непосредственно из исходного кода (если у вас есть).
Чтобы добавить мои 2 цента; информация о параметре доступна в файле класса "для отладки", когда вы используете javac -g для компиляции источника. И он доступен для APT, но вам понадобится аннотация, которая вам не нужна. (Кто-то обсуждал что-то подобное 4-5 лет назад здесь: http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0)
В целом короткое замыкание невозможно получить, если вы не работаете непосредственно с исходными файлами (аналогично тому, что делает APT во время компиляции).
если вы используете eclipse, см. ниже изображение, чтобы компилятор мог хранить информацию о параметрах метода
Как пояснил @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 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 class loader cannot find the bytecode that defined the constructor 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;
}
}
Вот пример с unit test.
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, различно, но это то, что можно легко устранить.