Làm thế nào để lặp qua một thuộc tính Class trong Java?


85

Làm cách nào để tôi có thể lặp động các thuộc tính lớp trong java.

Ví dụ:

public class MyClass{
    private type1 att1;
    private type2 att2;
    ...

    public void function(){
        for(var in MyClass.Attributes){
            System.out.println(var.class);
        }
    }
}

điều này có khả thi trong Java không?

Câu trả lời:


100

Không có hỗ trợ ngôn ngữ để làm những gì bạn đang yêu cầu.

Bạn có thể truy cập phản xạ các thành viên của một kiểu tại thời điểm chạy bằng cách sử dụng phản chiếu (ví dụ: với Class.getDeclaredFields()để lấy một mảng Field), nhưng tùy thuộc vào những gì bạn đang cố gắng làm, đây có thể không phải là giải pháp tốt nhất.

Xem thêm

Câu hỏi liên quan


Thí dụ

Đây là một ví dụ đơn giản để chỉ một số điều mà phản xạ có thể thực hiện.

import java.lang.reflect.*;

public class DumpFields {
    public static void main(String[] args) {
        inspect(String.class);
    }
    static <T> void inspect(Class<T> klazz) {
        Field[] fields = klazz.getDeclaredFields();
        System.out.printf("%d fields:%n", fields.length);
        for (Field field : fields) {
            System.out.printf("%s %s %s%n",
                Modifier.toString(field.getModifiers()),
                field.getType().getSimpleName(),
                field.getName()
            );
        }
    }
}

Đoạn mã trên sử dụng sự phản chiếu để kiểm tra tất cả các trường được khai báo của class String; nó tạo ra kết quả sau:

7 fields:
private final char[] value
private final int offset
private final int count
private int hash
private static final long serialVersionUID
private static final ObjectStreamField[] serialPersistentFields
public static final Comparator CASE_INSENSITIVE_ORDER

Phiên bản Java thứ 2 hiệu quả, Mục 53: Thích giao diện phản chiếu

Đây là những đoạn trích từ cuốn sách:

Cho một Classđối tượng, bạn có thể có được Constructor, MethodFieldtrường hợp đại diện các nhà thầu, các phương pháp và các lĩnh vực của lớp. [Họ] cho phép bạn thao túng các đối tác cơ bản của họ một cách phản xạ . Sức mạnh này, tuy nhiên, đi kèm với một cái giá:

  • Bạn mất tất cả các lợi ích của việc kiểm tra thời gian biên dịch.
  • Mã cần thiết để thực hiện truy cập phản chiếu rất vụng về và dài dòng.
  • Hiệu suất bị ảnh hưởng.

Theo quy định, các đối tượng không nên được truy cập phản chiếu trong các ứng dụng bình thường trong thời gian chạy.

Có một số ứng dụng phức tạp yêu cầu phản ánh. Các ví dụ bao gồm [... có chủ đích bị bỏ qua ...] Nếu bạn có bất kỳ nghi ngờ nào về việc liệu ứng dụng của mình có thuộc một trong các loại này hay không, thì có thể là không.


trong thực tế, theo giá trị của các trường, tôi muốn ghi hay không ghi giá trị của từng trường?
Zakaria

@Zakaria: vâng, bạn có thể thực hiện điều này bằng sự phản chiếu, nhưng tùy thuộc vào việc bạn đang cố gắng làm gì, có nhiều thiết kế tốt hơn.
polygenelubricants

Trên thực tế, tôi có một báo cáo cần tạo từ một lớp và tùy thuộc vào giá trị của các trường lớp mà tôi muốn đặt hoặc không đặt các trường đó, thiết kế tốt nhất mà tôi có thể triển khai là gì?
Zakaria

1
@Zakaria: hãy tiếp tục và thử phản ánh sau đó. Ở Field.getđó bạn có thể sử dụng để đọc các giá trị của một trường một cách phản ánh. Nếu có private, bạn có thể vượt qua điều đó với setAccessible. Vâng, phản xạ rất mạnh mẽ và nó cho phép bạn thực hiện những việc như kiểm tra privatecác trường, ghi đè finalcác trường, gọi các hàm privatetạo, v.v. Nếu bạn cần hỗ trợ thêm về khía cạnh thiết kế, có lẽ bạn nên viết một câu hỏi mới với nhiều thông tin hơn. Có lẽ bạn không cần nhiều thuộc tính này ngay từ đầu (ví dụ: sử dụng enumhoặc List, v.v.).
polygenelubricants

1
generics không được sử dụng ở đây. chỉ làmstatic void inspect(Class<?> klazz)
user102008

45

Truy cập trực tiếp các trường không thực sự tốt trong java. Tôi khuyên bạn nên tạo các phương thức getter và setter cho các trường bean của bạn và sau đó sử dụng các lớp Intros Inspector và BeanInfo từ gói java.beans.

MyBean bean = new MyBean();
BeanInfo beanInfo = Introspector.getBeanInfo(MyBean.class);
for (PropertyDescriptor propertyDesc : beanInfo.getPropertyDescriptors()) {
    String propertyName = propertyDesc.getName();
    Object value = propertyDesc.getReadMethod().invoke(bean);
}

Có cách nào để chỉ lấy thuộc tính của bean, không phải của lớp không? MyBean.class tránh tức là trả về bởi getPropertyDescriptors
hbprotoss

@hbprotoss nếu tôi hiểu bạn đúng, bạn có thể kiểm tra propertyDesc.getReadMethod().getDeclaringClass() != Object.classhoặc bạn có thể chỉ định một lớp để dừng phân tích làm tham số thứ hai như thế nào getBeanInfo(MyBean.class, Object.class).
Jörn Horstmann

20

Mặc dù tôi đồng ý với câu trả lời của Jörn nếu lớp của bạn phù hợp với thông số JavaBeabs, đây là một giải pháp thay thế tốt nếu không và bạn sử dụng Spring.

Spring có một lớp tên là ReflectionUtils cung cấp một số chức năng rất mạnh, bao gồm doWithFields (lớp, gọi lại) , một phương thức kiểu khách cho phép bạn lặp qua các trường lớp bằng cách sử dụng một đối tượng gọi lại như sau:

public void analyze(Object obj){
    ReflectionUtils.doWithFields(obj.getClass(), field -> {

        System.out.println("Field name: " + field.getName());
        field.setAccessible(true);
        System.out.println("Field value: "+ field.get(obj));

    });
}

Nhưng đây là một cảnh báo: lớp được gắn nhãn là "chỉ sử dụng nội bộ", thật đáng tiếc nếu bạn hỏi tôi


13

Cách đơn giản để lặp qua các trường lớp và lấy giá trị từ đối tượng:

 Class<?> c = obj.getClass();
 Field[] fields = c.getDeclaredFields();
 Map<String, Object> temp = new HashMap<String, Object>();

 for( Field field : fields ){
      try {
           temp.put(field.getName().toString(), field.get(obj));
      } catch (IllegalArgumentException e1) {
      } catch (IllegalAccessException e1) {
      }
 }

Một lớp có lẽ hoặc bất kỳ đối tượng nào.
Rickey

@Rickey Có cách nào đặt giá trị mới cho các thuộc tính bằng cách lặp lại từng trường không?
hyperfkcb

@hyperfkcb Bạn có thể nhận được một ngoại lệ sửa đổi khi cố gắng thay đổi giá trị của thuộc tính / trường mà bạn đang lặp lại.
Rickey

6

Java có Reflection (java.reflection. *), Nhưng tôi khuyên bạn nên xem xét một thư viện như Apache Beanutils, nó sẽ làm cho quá trình này ít tốn kém hơn nhiều so với việc sử dụng phản chiếu trực tiếp.


0

Đây là một giải pháp sắp xếp các thuộc tính theo thứ tự bảng chữ cái và in tất cả chúng cùng với các giá trị của chúng:

public void logProperties() throws IllegalArgumentException, IllegalAccessException {
  Class<?> aClass = this.getClass();
  Field[] declaredFields = aClass.getDeclaredFields();
  Map<String, String> logEntries = new HashMap<>();

  for (Field field : declaredFields) {
    field.setAccessible(true);

    Object[] arguments = new Object[]{
      field.getName(),
      field.getType().getSimpleName(),
      String.valueOf(field.get(this))
    };

    String template = "- Property: {0} (Type: {1}, Value: {2})";
    String logMessage = System.getProperty("line.separator")
            + MessageFormat.format(template, arguments);

    logEntries.put(field.getName(), logMessage);
  }

  SortedSet<String> sortedLog = new TreeSet<>(logEntries.keySet());

  StringBuilder sb = new StringBuilder("Class properties:");

  Iterator<String> it = sortedLog.iterator();
  while (it.hasNext()) {
    String key = it.next();
    sb.append(logEntries.get(key));
  }

  System.out.println(sb.toString());
}
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.