Как получить имена параметров конструкторов объекта (отражение)?

Скажем, я как-то получил ссылку на объект из другого класса:

Object myObj = anObject;

Теперь я могу получить класс этого объекта:

Class objClass = myObj.getClass();

Теперь я могу получить все конструкторы этого класса:

Constructor[] constructors = objClass.getConstructors();

Теперь я могу зациклить каждый конструктор:

if (constructors.length > 0)
{
    for (int i = 0; i < constructors.length; i++)
    {
        System.out.println(constructors[i]);
    }
}

Это уже дает мне хорошее резюме конструктора, например, конструктор public Test(String paramName) отображается как public Test(java.lang.String)

Однако вместо того, чтобы дать мне тип класса, я хочу получить имя параметра.. в данном случае "paramName". Как бы я это сделал? Я попробовал следующее безуспешно:

if (constructors.length > 0)
    {
        for (int iCon = 0; iCon < constructors.length; iCon++)
        {
            Class[] params = constructors[iCon].getParameterTypes();
            if (params.length > 0)
            {
                for (int iPar = 0; iPar < params.length; iPar++)
                {
                    Field fields[] = params[iPar].getDeclaredFields();
                    for (int iFields = 0; iFields < fields.length; iFields++)
                    {
                        String fieldName = fields[i].getName();
                        System.out.println(fieldName);
                    }                                       
                }
            }
        }
    }

К сожалению, это не дает мне ожидаемого результата. Может ли кто-нибудь сказать мне, как я должен делать это или что я делаю неправильно? Спасибо!

3 ответа

Решение

Эта информация теряется после компиляции и не может быть получена во время выполнения.

Как упоминалось в комментариях к ответу Романа, имена параметров могут быть получены, если компилятор включал символы отладки, но не через стандартный API-интерфейс Java Reflection. Ниже приведен пример, иллюстрирующий, как вы можете получить имена параметров через символы отладки, используя библиотеку байт-кода ASM:

/**
 * 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 constructor
 * @return 
 * @throws IOException
 */
public static List<String> getParameterNames(Constructor<?> constructor) throws IOException {
    Class<?> declaringClass = constructor.getDeclaringClass();
    ClassLoader declaringClassLoader = declaringClass.getClassLoader();

    Type declaringType = Type.getType(declaringClass);
    String constructorDescriptor = Type.getConstructorDescriptor(constructor);
    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("<init>") && 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 = 0; i < argumentTypes.length; i++) {
                // The first local variable actually represents the "this" object
                parameterNames.add(localVariables.get(i + 1).name);
            }

            return parameterNames;
        }
    }

    return null;
}

В этом примере используется дерево API библиотеки ASM. Если скорость и память драгоценны, вы можете рефакторинг примера, чтобы использовать вместо него API посетителя.

Попробуйте https://github.com/paul-hammant/paranamer

О, ради бога, ТАК, действительно, вы заставите меня ввести не менее 30 символов, чтобы отредактировать существующий ответ, чтобы он был правильным.

Другие вопросы по тегам