Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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();
            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!

like image 200
Tom Avatar asked Apr 28 '10 12:04

Tom


People also ask

Can I obtain method parameter name using Java reflection?

You can obtain the names of the formal parameters of any method or constructor with the method java. lang. reflect.

Which method is used to fetch the parameter types using method parameter reflection?

reflect package is used to fetch the parameter types using method parameter reflection. Reflection is a process of analyzing and modifying all capabilities of class at runtime.

What is parameters in constructor?

A parameterized constructor accepts parameters with which you can initialize the instance variables. Using parameterized constructor, you can initialize the class variables dynamically at the time of instantiating the class with distinct values.

What name is given to constructors with different sets of parameters?

Parameterized Constructor – A constructor is called Parameterized Constructor when it accepts a specific number of parameters. To initialize data members of a class with distinct values. In the above example, we are passing a string and an integer to the object.


2 Answers

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.

like image 111
Adam Paynter Avatar answered Oct 08 '22 18:10

Adam Paynter


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.

like image 41
Duncan McGregor Avatar answered Oct 08 '22 18:10

Duncan McGregor