2010-04-28 37 views
16

Nói rằng tôi bằng cách nào đó có một tham chiếu đối tượng từ một lớp khác:Làm thế nào để có được tên tham số của một nhà xây dựng của đối tượng (phản ánh)?

Object myObj = anObject; 

Bây giờ tôi có thể nhận được các lớp của đối tượng này:

Class objClass = myObj.getClass(); 

Bây giờ, tôi có thể nhận được tất cả các nhà xây dựng của lớp này:

Constructor[] constructors = objClass.getConstructors(); 

Bây giờ, tôi có thể lặp mỗi constructor:

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

Điều này đã mang lại cho tôi bản tóm tắt tốt về hàm tạo, ví dụ: Kiểm tra công khai hàm (String paramName) được hiển thị là Kiểm tra công khai (java.lang.String)

Thay vì cho tôi loại lớp, Tôi muốn lấy tên của tham số .. trong trường hợp này là "paramName". Tôi sẽ làm như thế nào? Tôi đã thử những điều sau đây mà không thành công:

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

Thật không may, điều này không mang lại kết quả mong đợi. Bất cứ ai có thể cho tôi biết làm thế nào tôi nên làm điều này hoặc những gì tôi đang làm sai? Cảm ơn!

+1

Điều này có thể thông qua phản ánh trong ** Java 8 **, xem [câu trả lời SO này] (http://stackoverflow.com/a/21455958/573057) - được tìm thấy bằng cách đọc tài liệu về [paranamer] (https: //github.com/paul-hammant/paranamer) từ câu trả lời của Duncan McGregor dưới đây. – earcam

Trả lời

11

Thông tin này bị mất sau khi biên dịch và không thể truy xuất được khi chạy.

+0

Bạn có chắc chắn không? Vì vậy, một nhà xây dựng được xử lý khác với các phương pháp khác? – Tom

+0

@Tom: Cả nhà xây dựng lẫn phương pháp đều không giữ lại tên thông số sau khi biên dịch. –

+0

Tại http://stackoverflow.com/questions/471693/using-reflection-to-get-method-name-and-parameters Jon Skeet nói trong một câu trả lời "Bạn không thể nhận được các giá trị tham số phương thức từ sự phản chiếu. Bạn có thể nhận được tên và loại tham số, nhưng không phải là thông số. " - đây là điều khiến tôi tin rằng điều đó là có thể, nhưng tôi đoán anh ấy đã sai. Cảm ơn mặc dù. – Tom

16

Như đã đề cập trong các ý kiến ​​trên Roman's answer, tên tham số có thể được truy xuất nếu trình biên dịch bao gồm các biểu tượng gỡ lỗi, mặc dù không thông qua API phản chiếu Java chuẩn. Dưới đây là một ví dụ minh họa làm thế nào bạn có thể có được tên tham số thông qua những biểu tượng gỡ lỗi bằng cách sử dụng 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; 
} 

Ví dụ này sử dụng thư viện ASM của tree API. Nếu tốc độ và bộ nhớ là quý giá, bạn có thể cấu trúc lại ví dụ để sử dụng số visitor API thay thế.

12

Hãy thử https://github.com/paul-hammant/paranamer

Oh cho sự tốt lành vì SO, thực sự, bạn sẽ làm cho tôi nhập ít nhất 30 ký tự để chỉnh sửa một câu trả lời hiện có để làm cho nó đúng.

+0

Cảm ơn mẹo! Hoạt động tuyệt vời. – Ciddan

+0

Liên kết yêu cầu đăng nhập. – javamonkey79

+0

Codehaus đã bị đóng cửa, đã chỉ đến nhà Github mới –

Các vấn đề liên quan