【问题标题】:How to get the parameter names of an object's constructors (reflection)? [duplicate]如何获取对象构造函数的参数名称(反射)? [复制]
【发布时间】:2011-02-13 08:21:06
【问题描述】:

假设我以某种方式从其他类获得了对象引用:

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);
                    }                                       
                }
            }
        }
    }

不幸的是,这并没有给我预期的结果。谁能告诉我应该怎么做或者我做错了什么?谢谢!

【问题讨论】:

  • 这可以通过 Java 8 中的反射实现,请参阅 this SO answer - 通过阅读下面 Duncan McGregor 的回答中的 paranamer 文档找到。

标签: java reflection parameters constructor


【解决方案1】:

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

哦,看在上帝的份上,真的,你要让我输入至少 30 个字符来编辑现有答案以使其正确。

【讨论】:

  • 感谢您的提示!效果很好。
  • 链接需要登录。
  • Codehaus 已关闭,已指向新的 Github 主页
  • 这是一个干净简单的解决方案。只要确保直接使用 BytecodeReadingParanamer 而不是默认的。
【解决方案2】:

正如Roman's answer 上的 cmets 中所述,如果编译器包含调试符号,则可以检索参数名称​​,尽管不是通过标准 Java 反射 API。下面是一个示例,说明如何使用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;
}

此示例使用 ASM 库的 tree API。如果速度和内存很宝贵,您可以重构示例以改用其visitor API。

【讨论】:

    【解决方案3】:

    此信息在编译后丢失,无法在运行时检索。

    【讨论】:

    • 你确定吗?那么构造函数的处理方式与其他方法不同吗?
    • @Tom:构造函数和方法在编译后都不会保留参数名称。
    • @Tom:Jon 指的是编译器可以选择在类文件中包含的调试符号。从这个意义上说,参数名称在技术上是可用的,尽管不是通过反射 API。
    • 好的,谢谢。接受这个答案。
    猜你喜欢
    • 2018-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-23
    • 1970-01-01
    • 2021-05-31
    • 2010-10-11
    相关资源
    最近更新 更多