Получение точного имени параметра формы Java-метод [duplicate]

При использовании cmake для меня ни один из файлов редактирования файлов и ссылки не работал, поэтому я скомпилирован с использованием флагов, которые указывают версию gcc / g ++. cmake -DCMAKE_C_COMPILER=gcc-6 -DCMAKE_CXX_COMPILER=g++-6 ..

Работал как шарм.

98
задан Geo 10 February 2010 в 16:10
поделиться

15 ответов

Как сказал @Bozho, это возможно, если во время компиляции включена отладочная информация. Здесь есть хороший ответ ...

Как получить имена параметров конструкторов объекта (отражение)? by @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, различно, но это то, что можно легко устранить.
70
ответ дан Community 18 August 2018 в 17:33
поделиться
  • 1
    Возможно ли это с интерфейсами? Я все еще не мог найти способ получить имена параметров интерфейса. – Cemo 4 April 2014 в 12:29
  • 2
    @Cemo: вы могли найти способ получить имена параметров интерфейса? – Vaibhav Gupta 30 September 2015 в 13:41
70
ответ дан Community 7 September 2018 в 02:07
поделиться
72
ответ дан Community 30 October 2018 в 06:25
поделиться

В 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>

Для получения дополнительной информации см. следующие ссылки:

  1. Официальное руководство по Java: получение имен параметров метода
  2. JEP 118: доступ к именам параметров во время выполнения
  3. Javadoc для класса параметра
70
ответ дан bernie 18 August 2018 в 17:33
поделиться
  • 1
    Я не знаю, изменили ли они аргументы для плагина компилятора, но с последним (3.5.1 на данный момент) мне пришлось сделать, чтобы использовать аргументы компилятора в разделе конфигурации: & lt; configuration & gt; & Lt; compilerArgs & GT; & Lt; Arg & GT; & л-параметров; / Arg & GT; & Lt; / compilerArgs & GT; & Lt; / конфигурации & GT; – max 15 April 2016 в 14:40
  • 2
    Это не будет работать, если методы перегружены. – jainilvachhani 17 July 2018 в 07:39

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

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

0
ответ дан danben 18 August 2018 в 17:33
поделиться
  • 1
    Имя параметра не только полезно для компилятора, они также передают информацию потребителю библиотеки, предположим, что я написал класс StrangeDate, у которого был метод add (int day, int hour, int minute), если вы решили использовать его и нашли метод add (int arg0, int arg1, int arg2), насколько это было бы полезно? – David Waters 22 February 2010 в 17:50
  • 2
    Извините, вы полностью не поняли мой ответ. Пожалуйста, перечитайте это в контексте вопроса. – danben 23 February 2010 в 00:37
  • 3
    Приобретение имен параметров является большой выгодой для отражения этого метода. Это не только полезно для компилятора, даже в контексте вопроса. – corsiKa 15 January 2014 в 23:48

см. org.springframework.core.DefaultParameterNameDiscoverer class

DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] params = discoverer.getParameterNames(MathUtils.class.getMethod("isPrime", Integer.class));
5
ответ дан Denis Danilkovich 18 August 2018 в 17:33
поделиться

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

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

0
ответ дан Elister 18 August 2018 в 17:33
поделиться

Это возможно, и Spring MVC 3 делает это, но я не нашел времени, чтобы посмотреть, как именно.

Согласование имен параметров метода с именами переменных шаблона URI может быть только если ваш код скомпилирован с включенной отладкой. Если вы не включили отладку, вы должны указать имя имени переменной шаблона URI в аннотации @PathVariable, чтобы связать разрешенное значение имени переменной с параметром метода. Например:

Взято из документации весны

3
ответ дан flybywire 18 August 2018 в 17:33
поделиться
  • 1
    org.springframework.core.Conventions.getVariableName (), но я полагаю, что у вас должен быть cglib как зависимость – Radu Toader 20 June 2015 в 08:27

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

1
ответ дан Hazim 18 August 2018 в 17:33
поделиться

Вы можете получить метод с отражением и определить его типы аргументов. Проверьте http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html#getParameterTypes%28%29

Однако вы не можете указать имя используемого аргумента.

5
ответ дан Johnco 18 August 2018 в 17:33
поделиться
  • 1
    Поистине, это все возможно. Однако его вопрос был явно о имени параметра . Проверьте topictitle. – BalusC 10 February 2010 в 16:17
  • 2
    И я цитирую: «Однако вы не можете указать имя используемого аргумента. & Quot; просто прочитайте мой ответ -_- – Johnco 10 February 2010 в 16:23
  • 3
    Вопрос не был сформулирован так, как он не знал о получении типа. – BalusC 10 February 2010 в 16:25

Да. Код должен быть скомпилирован с помощью компилятора, совместимого с 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());
   }
}
7
ответ дан Karol Król 18 August 2018 в 17:33
поделиться
  • 1
    Пробовал это, и это сработало! Один совет, однако, мне пришлось перестроить ваш проект, чтобы эти конфигурации компилятора вступили в силу. – ishmaelMakitla 12 January 2018 в 08:27

См. java.beans.ConstructorProperties , это аннотация, предназначенная для выполнения именно этого.

3
ответ дан Markus Jevring 18 August 2018 в 17:33
поделиться

Библиотека Paranamer была создана для решения этой же проблемы.

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

Другой способ заключается в том, чтобы он вставлял частный статический член в байт-код класса после его компиляции, но прежде чем он будет помещен в банку. Затем он использует отражение для извлечения этой информации из класса во время выполнения.

https://github.com/paul-hammant/paranamer

У меня было проблемы с использованием этой библиотеки, но я все-таки получил работу в конце. Я надеюсь сообщить о проблемах сопровождающему.

13
ответ дан Sarel Botha 18 August 2018 в 17:33
поделиться
  • 1
    Я пытаюсь использовать paranamer внутри андроида apk. Но я получаю ParameterNAmesNotFoundException – Rilwan 9 October 2017 в 10:53

Итак, вы должны уметь:

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
        }
2
ответ дан tim_yates 18 August 2018 в 17:33
поделиться
  • 1
    Я также не хочу указывать имя метода aMethod. Я хочу получить его для всех методов в классе. – snoop 3 March 2016 в 12:35
  • 2
    @snoop добавил пример получения каждого несинтетического метода – tim_yates 3 March 2016 в 12:40
  • 3
    Можем ли мы использовать antlr для получения имен параметров для этого? – snoop 3 March 2016 в 12:49

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

Не самый чистый но он выполняет свою работу. Некоторые веб-службы на самом деле делают это, чтобы сохранить имена параметров (то есть: развертывание WSs с помощью стеклянной рыбы).

3
ответ дан WhyNotHugo 18 August 2018 в 17:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: