开发者

How to get the parameter names of an object's constructors (reflection)? [duplicate]

开发者 https://www.devze.com 2022-12-28 05:58 出处:网络
This question already has answers here: Can I obtain method parameter name using Java reflection? (15 answers)
This question already has answers here: Can I obtain method parameter name using Java reflection? (15 answers) Closed 9 years ago.

Say I somehow got an object reference from an other class:

Object myObj = anObject;

Now I can get the class of this object:

Class objClass = myObj.getClass();

Now, I can get all constructors of this class:

Constructor[] constructors = objClass.getConstructors();

Now, I can loop every constructor:

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

This is already giving me a good summary of the constructor, for example a constructor public Test(String paramName) is shown as public Test(java.lang.String)

Instead of giving me the class type however, I want to get the name of the parameter.. in this case "paramName". How would I do that? I tried the following without success:

if (constructors.length > 0)
    {
        for (int iCon = 0; iCon < constructors.length; iCon++)
        {
            Class[] params = constructors[iCon].getParameterTypes(开发者_StackOverflow);
            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);
                    }                                       
                }
            }
        }
    }

Unfortunately, this is not giving me the expected result. Could anyone tell me how I should do this or what I am doing wrong? Thanks!


As mentioned in the comments on Roman's answer, the parameter names can be retrieved if the compiler included debugging symbols, though not through the standard Java Reflection API. Below is an example illustrating how you could obtain parameter names via the debugging symbols using the ASM bytecode library:

/**
 * 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;
}

This example uses the ASM library's tree API. If speed and memory are precious, you can refactor the example to use its visitor API instead.


This information is lost after compilation and can't be retrieved at runtime.


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

Oh for goodness sake SO, really, you're going to make me enter at least 30 characters to edit an existing answer to make it correct.

0

精彩评论

暂无评论...
验证码 换一张
取 消