Могу ли я получить имя параметра метода, используя отражение Java? - PullRequest
111 голосов
/ 10 февраля 2010

Если у меня есть такой класс:

public class Whatever
{
  public void aMethod(int aParam);
}

есть ли способ узнать, что aMethod использует параметр с именем aParam, который имеет тип int?

Ответы [ 14 ]

2 голосов
/ 03 марта 2016

Так что вы должны быть в состоянии сделать:

Whatever.declaredMethods
        .find { it.name == 'aMethod' }
        .parameters
        .collect { "$it.type : $it.name" }

Но вы, вероятно, получите такой список:

["int : arg0"]

Я полагаю, это будет исправлено в Groovy 2.5 +

Итак, в настоящее время ответ:

  • Если это класс Groovy, то нет, вы не можете получить имя, но вы должны это сделать в будущем.
  • Если это класс Java, скомпилированный в Java 8, вы должны это сделать.

Смотри также:


Для каждого метода, тогда что-то вроде:

Whatever.declaredMethods
        .findAll { !it.synthetic }
        .collect { method -> 
            println method
            method.name + " -> " + method.parameters.collect { "[$it.type : $it.name]" }.join(';')
        }
        .each {
            println it
        }
0 голосов
/ 04 января 2017

Как сказал @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

Предостережения

  • Я немного изменил исходное решение @AdamPaynter, чтобы оно работало для методов.Если я правильно понял, его решение работает только с конструкторами.
  • Это решение не работает с static методами.Это связано с тем, что в этом случае число возвращаемых ASM аргументов отличается, но это легко исправить.
0 голосов
/ 10 февраля 2010

Чтобы добавить мои 2 цента;Информация о параметрах доступна в файле класса "для отладки", когда вы используете javac -g для компиляции исходного кода.И это доступно для APT, но вам понадобятся аннотации, так что бесполезно для вас.(Кто-то обсуждал что-то подобное 4-5 лет назад здесь: http://forums.java.net/jive/thread.jspa?messageID=13467&tstart=0)

В общем, вы не можете получить его, если не работаете с исходными файлами напрямую (аналогично тому, что APT делает при компиляциивремя).

0 голосов
/ 10 февраля 2010

Имена параметров полезны только для компилятора. Когда компилятор генерирует файл класса, имена параметров не включаются - список аргументов метода состоит только из числа и типов его аргументов. Поэтому было бы невозможно получить имя параметра, используя отражение (как отмечено в вашем вопросе) - оно нигде не существует.

Однако, если использование отражения не является жестким требованием, вы можете получить эту информацию непосредственно из исходного кода (если она у вас есть).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...