Phản xạ Java: Làm thế nào để có được tên của một biến?


139

Sử dụng Java Reflection, có thể lấy tên của một biến cục bộ không? Ví dụ: nếu tôi có cái này:

Foo b = new Foo();
Foo a = new Foo();
Foo r = new Foo();

có thể thực hiện một phương thức có thể tìm thấy tên của các biến đó không, như vậy:

public void baz(Foo... foos)
{
    for (Foo foo: foos) {
        // Print the name of each foo - b, a, and r
        System.out.println(***); 
    }
}

EDIT: Câu hỏi này khác với Có cách nào trong Java để tìm tên của biến được truyền cho hàm không? trong đó nó hoàn toàn đặt câu hỏi về việc liệu người ta có thể sử dụng sự phản chiếu để xác định tên của một biến cục bộ hay không, trong khi câu hỏi khác (bao gồm cả câu trả lời được chấp nhận) tập trung hơn vào việc kiểm tra các giá trị của các biến.


11
Tất cả các câu trả lời tuyệt vời! Cảm ơn tất cả mọi người đã trả lời và bình luận - đây là một cuộc thảo luận thú vị và sâu sắc.
David Koelle


Điều đó là có thể. Xem [ý chính] [1] của tôi. Hoạt động cho JDK 1.1 đến JDK 7. [1]: gist.github.com/2011728
Wendal Chen


3
Không phải là một bản sao, và cập nhật câu hỏi của tôi để giải thích tại sao. Nếu bất cứ điều gì, câu hỏi khác là một bản sao (hoặc trường hợp đặc biệt) của câu hỏi này!
David Koelle

Câu trả lời:


65

Kể từ Java 8, một số thông tin tên biến cục bộ có sẵn thông qua sự phản chiếu. Xem phần "Cập nhật" bên dưới.

Thông tin đầy đủ thường được lưu trữ trong các tập tin lớp. Tối ưu hóa một thời gian biên dịch là loại bỏ nó, tiết kiệm không gian (và cung cấp một số ám ảnh). Tuy nhiên, khi nó có mặt, mỗi phương thức có một thuộc tính bảng biến cục bộ liệt kê loại và tên của các biến cục bộ và phạm vi các hướng dẫn trong đó chúng nằm trong phạm vi.

Có lẽ một thư viện kỹ thuật mã byte như ASM sẽ cho phép bạn kiểm tra thông tin này khi chạy. Nơi hợp lý duy nhất tôi có thể nghĩ đến khi cần thông tin này là trong một công cụ phát triển và vì vậy kỹ thuật mã byte có thể cũng hữu ích cho các mục đích khác.


Cập nhật: Hỗ trợ hạn chế cho điều này đã được thêm vào Java 8. Tên tham số (một lớp đặc biệt của biến cục bộ) hiện có sẵn thông qua sự phản chiếu. Trong số các mục đích khác, điều này có thể giúp thay thế @ParameterNamecác chú thích được sử dụng bởi các thùng chứa phụ thuộc.


49

Điều đó là không thể. Tên biến không được giao tiếp trong Java (và cũng có thể bị xóa do tối ưu hóa trình biên dịch).

EDIT (liên quan đến ý kiến):

Nếu bạn lùi lại ý tưởng phải sử dụng nó làm tham số chức năng, thì đây là một giải pháp thay thế (mà tôi sẽ không sử dụng - xem bên dưới):

public void printFieldNames(Object obj, Foo... foos) {
    List<Foo> fooList = Arrays.asList(foos);
    for(Field field : obj.getClass().getFields()) {
         if(fooList.contains(field.get()) {
              System.out.println(field.getName());
         }
    }
}

Sẽ có vấn đề nếu a == b, a == r, or b == rhoặc có các lĩnh vực khác có cùng tham chiếu.

EDIT bây giờ không cần thiết vì câu hỏi đã được làm rõ


Sau đó, làm thế nào để bạn giải thích điều này: java.sun.com/javase/6/docs/api/java/lang/reflect/Field.html ?
Lập trình viên ngoài vòng pháp luật

1
-1: Tôi nghĩ bạn đã hiểu lầm. @David là sau các trường, không phải biến cục bộ. Các biến cục bộ thực sự không có sẵn thông qua API Reflection.
Luke Woodward

Tôi nghĩ Pourquoi Litytestdata là đúng. Rõ ràng các trường không thể được tối ưu hóa đi, vì vậy Marcel J. phải nghĩ về các biến cục bộ.
Michael Myers

3
@David: Bạn cần chỉnh sửa để làm rõ rằng bạn có nghĩa là các trường hơn là các biến cục bộ. Câu hỏi ban đầu đưa ra mã khai báo b, a và r là các biến cục bộ.
Jason S

7
Tôi có nghĩa là các biến cục bộ và tôi đã chỉnh sửa câu hỏi để phản ánh điều đó. Tôi nghĩ rằng có thể không thể có được tên biến, nhưng tôi đoán tôi đã hỏi SO trước khi xem xét nó là không thể.
David Koelle

30

( Chỉnh sửa: hai câu trả lời trước đã bị xóa, một câu trả lời cho câu hỏi khi nó đứng trước các chỉnh sửa và một câu trả lời, nếu không hoàn toàn sai, ít nhất là gần với nó. )

Nếu bạn biên dịch với thông tin gỡ lỗi trên ( javac -g), tên của các biến cục bộ sẽ được giữ trong tệp. Class. Ví dụ, lấy lớp đơn giản này:

class TestLocalVarNames {
    public String aMethod(int arg) {
        String local1 = "a string";
        StringBuilder local2 = new StringBuilder();
        return local2.append(local1).append(arg).toString();
    }
}

Sau khi biên dịch với javac -g:vars TestLocalVarNames.java, tên của các biến cục bộ hiện có trong tệp. Class. javap's -lcờ ( 'In số dòng và bảng biến cục bộ') có thể hiển thị chúng.

javap -l -c TestLocalVarNames trình diễn:

class TestLocalVarNames extends java.lang.Object{
TestLocalVarNames();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      5      0    this       LTestLocalVarNames;

public java.lang.String aMethod(int);
  Code:
   0:   ldc     #2; //String a string
   2:   astore_2
   3:   new     #3; //class java/lang/StringBuilder
   6:   dup
   7:   invokespecial   #4; //Method java/lang/StringBuilder."<init>":()V
   10:  astore_3
   11:  aload_3
   12:  aload_2
   13:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   16:  iload_1
   17:  invokevirtual   #6; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   20:  invokevirtual   #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   23:  areturn

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      24      0    this       LTestLocalVarNames;
   0      24      1    arg       I
   3      21      2    local1       Ljava/lang/String;
   11      13      3    local2       Ljava/lang/StringBuilder;
}

Thông số kỹ thuật VM giải thích những gì chúng ta đang thấy ở đây:

§4.7.9 LocalVariableTableThuộc tính :

Các LocalVariableTablethuộc tính là một tùy chọn thuộc tính chiều dài thay đổi của một Code(§4.7.3) thuộc tính. Nó có thể được sử dụng bởi các trình gỡ lỗi để xác định giá trị của một biến cục bộ nhất định trong quá trình thực thi một phương thức.

Việc LocalVariableTablelưu trữ tên và loại của các biến trong mỗi vị trí, do đó có thể khớp chúng với mã byte. Đây là cách trình gỡ lỗi có thể thực hiện "Đánh giá biểu thức".

Như erickson đã nói, tuy nhiên, không có cách nào để truy cập bảng này thông qua sự phản chiếu bình thường. Nếu bạn vẫn quyết tâm thực hiện việc này, tôi tin rằng Kiến trúc trình gỡ lỗi nền tảng Java (JPDA) sẽ giúp ích (nhưng tôi chưa bao giờ sử dụng nó cho mình).


1
Uh-oh, erickson đăng trong khi tôi đang chỉnh sửa và bây giờ tôi đang mâu thuẫn với anh ấy. Điều đó có nghĩa là tôi sai.
Michael Myers

Theo mặc định, javacđặt một bảng biến cục bộ trong lớp cho mỗi phương thức để hỗ trợ gỡ lỗi. Sử dụng -ltùy chọn để javapxem bảng biến cục bộ.
erickson

Không phải mặc định, có vẻ như. Tôi đã phải sử dụng javac -g:varsđể có được nó. (Tôi đã cố gắng để chỉnh sửa câu trả lời này cho ba tiếng đồng hồ qua, nhưng như tôi đã nói kết nối mạng của tôi đang gặp vấn đề, mà làm cho nó khó có thể nghiên cứu.)
Michael Myers

2
Bạn nói đúng, xin lỗi về điều đó. Đó là số dòng được "bật" theo mặc định.
erickson

15
import java.lang.reflect.Field;


public class test {

 public int i = 5;

 public Integer test = 5;

 public String omghi = "der";

 public static String testStatic = "THIS IS STATIC";

 public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
  test t = new test();
  for(Field f : t.getClass().getFields()) {
   System.out.println(f.getGenericType() +" "+f.getName() + " = " + f.get(t));
  }
 }

}

3
getDeclaredFields()cũng có thể được sử dụng trong trường hợp bạn muốn tên của trường tư nhân
coffeMug

10

Bạn có thể làm như thế này:

Field[] fields = YourClass.class.getDeclaredFields();
//gives no of fields
System.out.println(fields.length);         
for (Field field : fields) {
    //gives the names of the fields
    System.out.println(field.getName());   
}

Câu trả lời của bạn hoạt động tốt để có được tất cả các luồng. Để chỉ nhận một trường, khi tôi sử dụng: YourClass. Class.getDeclaredField ("field1"); Tôi nhận được NullPulum. Vấn đề trong việc sử dụng nó là gì? Tôi nên sử dụng phương thức getDeclaredField như thế nào?
Shashi Ranjan

0

Tất cả những gì bạn cần làm là tạo một mảng các trường và sau đó đặt nó vào lớp bạn muốn như hiển thị bên dưới.

Field fld[] = (class name).class.getDeclaredFields();   
for(Field x : fld)
{System.out.println(x);}

Ví dụ nếu bạn đã làm

Field fld[] = Integer.class.getDeclaredFields();
          for(Field x : fld)
          {System.out.println(x);}

bạn sẽ nhận được

public static final int java.lang.Integer.MIN_VALUE
public static final int java.lang.Integer.MAX_VALUE
public static final java.lang.Class java.lang.Integer.TYPE
static final char[] java.lang.Integer.digits
static final char[] java.lang.Integer.DigitTens
static final char[] java.lang.Integer.DigitOnes
static final int[] java.lang.Integer.sizeTable
private static java.lang.String java.lang.Integer.integerCacheHighPropValue
private final int java.lang.Integer.value
public static final int java.lang.Integer.SIZE
private static final long java.lang.Integer.serialVersionUID

0

cập nhật câu trả lời của @Marcel Jackwerth cho chung.

và chỉ làm việc với thuộc tính lớp, không làm việc với biến phương thức.

    /**
     * get variable name as string
     * only work with class attributes
     * not work with method variable
     *
     * @param headClass variable name space
     * @param vars      object variable
     * @throws IllegalAccessException
     */
    public static void printFieldNames(Object headClass, Object... vars) throws IllegalAccessException {
        List<Object> fooList = Arrays.asList(vars);
        for (Field field : headClass.getClass().getFields()) {
            if (fooList.contains(field.get(headClass))) {
                System.out.println(field.getGenericType() + " " + field.getName() + " = " + field.get(headClass));
            }
        }
    }

-1

xem ví dụ này:

PersonneTest pt=new PersonneTest();
System.out.println(pt.getClass().getDeclaredFields().length);
Field[]x=pt.getClass().getDeclaredFields();
System.out.println(x[1].getName());
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.