Sự khác biệt giữa getFields và getDeclaredFields trong phản chiếu Java là gì


194

Tôi hơi bối rối về sự khác biệt giữa getFieldsphương thức và getDeclaredFieldsphương thức khi sử dụng phản chiếu Java.

Tôi đọc nó getDeclaredFieldscung cấp cho bạn quyền truy cập vào tất cả các trường của lớp và getFieldschỉ trả về các trường công khai. Nếu đây là trường hợp, tại sao bạn sẽ không luôn luôn sử dụng getDeclaredFields?

Ai đó có thể vui lòng giải thích về điều này và giải thích sự khác biệt giữa hai phương pháp và khi nào / tại sao bạn muốn sử dụng phương pháp này không?


3
getFieldcó thể có được một trường được kế thừa từ một siêu lớp nhưng getDeclaredFieldkhông thể. getDeclaredFieldhạn chế chính nó vào lớp bạn gọi hàm trên.
dùng2336315

@ user2336315 là chính xác, tuy nhiên getFieldkhông thể truy cập các thành viên tư nhân
Madbreaks

Câu trả lời:


258

getFields ()

Tất cả các publictrường lên toàn bộ phân cấp lớp.

getDeclaredFields ()

Tất cả các trường, bất kể khả năng truy cập của chúng mà chỉ dành cho lớp hiện tại, không phải bất kỳ lớp cơ sở nào mà lớp hiện tại có thể được kế thừa từ đó.

Để có được tất cả các trường theo cấu trúc phân cấp, tôi đã viết hàm sau:

public static Iterable<Field> getFieldsUpTo(@Nonnull Class<?> startClass, 
                                   @Nullable Class<?> exclusiveParent) {

   List<Field> currentClassFields = Lists.newArrayList(startClass.getDeclaredFields());
   Class<?> parentClass = startClass.getSuperclass();

   if (parentClass != null && 
          (exclusiveParent == null || !(parentClass.equals(exclusiveParent)))) {
     List<Field> parentClassFields = 
         (List<Field>) getFieldsUpTo(parentClass, exclusiveParent);
     currentClassFields.addAll(parentClassFields);
   }

   return currentClassFields;
}

Các exclusiveParentlớp được cung cấp để ngăn chặn việc lấy các trường từ Object. Nó có thể là nullnếu bạn muốn các Objectlĩnh vực.

Để làm rõ, Lists.newArrayListđến từ ổi.

Cập nhật

FYI, đoạn mã trên được xuất bản trên GitHub trong dự án LibEx của tôi trong ReflectionUtils .


8
Câu trả lời tuyệt vời, nhưng cần lưu ý rằng các trường riêng trong siêu lớp không thể được sử dụng bởi các thể hiện của lớp hiện tại Field#getvà các phương thức tương tự. Nói cách khác, cách tiếp cận này không cho phép lớp hiện tại truy cập vào giao diện riêng của siêu lớp của nó, giống như cách biên dịch điển hình không.
FThndry

4
@Vulcan Đúng trừ khi mã được viết để sử dụng sự phản chiếu để thay đổi phạm vi thông qua setAccessiblevà không có Trình quản lý bảo mật tại chỗ
John B

Nit nhẹ, nên là "(bất kể khả năng truy cập)" không "(bất kể phạm vi)". Tất cả các trường có cùng phạm vi, cụ thể là cơ thể của lớp .
yshavit

@yshavit Cảm ơn. Cập nhật.
John B

1
Nó sẽ không. Vì privatecác trường chỉ có thể được truy cập thông qua getDeclaredFieldsđó là lớp cụ thể. Mỗi trường (thậm chí với cùng loại và tên) sẽ là các Fieldtrường hợp riêng biệt .
John B

7

Như đã đề cập, Class.getDeclaredField(String)chỉ nhìn vào các lĩnh vực Classmà bạn gọi nó.

Nếu bạn muốn tìm kiếm một Fieldtrong Classhệ thống phân cấp, bạn có thể sử dụng chức năng đơn giản này:

/**
 * Returns the first {@link Field} in the hierarchy for the specified name
 */
public static Field getField(Class<?> clazz, String name) {
    Field field = null;
    while (clazz != null && field == null) {
        try {
            field = clazz.getDeclaredField(name);
        } catch (Exception e) {
        }
        clazz = clazz.getSuperclass();
    }
    return field;
}

Điều này rất hữu ích để tìm một privatetrường từ một siêu lớp chẳng hạn. Ngoài ra, nếu bạn muốn sửa đổi giá trị của nó, bạn có thể sử dụng nó như thế này:

/**
 * Sets {@code value} to the first {@link Field} in the {@code object} hierarchy, for the specified name
 */
public static void setField(Object object, String fieldName, Object value) throws Exception {
    Field field = getField(object.getClass(), fieldName);
    field.setAccessible(true);
    field.set(object, value);
}

sửa đổi một chút để vẫn ném lỗi nếu hoàn toàn không tìm thấytry try { field = clazz.getDeclaredField(name); } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); if(clazz==null){ throw e; } }
Sven Dhaens

5

public Field[] getFields() throws SecurityException

Trả về một mảng chứa các đối tượng Trường phản ánh tất cả các trường công khai có thể truy cập của lớp hoặc giao diện được đại diện bởi đối tượng Class này. Các phần tử trong mảng trả về không được sắp xếp và không theo bất kỳ thứ tự cụ thể nào. Phương thức này trả về một mảng có độ dài 0 nếu lớp hoặc giao diện không có các trường công khai có thể truy cập hoặc nếu nó đại diện cho một lớp mảng, một kiểu nguyên thủy hoặc void.

Cụ thể, nếu đối tượng Class này đại diện cho một lớp, phương thức này trả về các trường công khai của lớp này và của tất cả các siêu lớp của nó. Nếu đối tượng Class này đại diện cho một giao diện, phương thức này trả về các trường của giao diện này và của tất cả các siêu giao diện của nó.

Trường độ dài ẩn cho lớp mảng không được phản ánh bởi phương thức này. Mã người dùng nên sử dụng các phương thức của lớp Array để thao tác với các mảng.


public Field[] getDeclaredFields() throws SecurityException

Trả về một mảng các đối tượng Trường phản ánh tất cả các trường được khai báo bởi lớp hoặc giao diện được đại diện bởi đối tượng Class này. Điều này bao gồm quyền truy cập công khai, được bảo vệ, mặc định (gói) và các trường riêng , nhưng loại trừ các trường được kế thừa . Các phần tử trong mảng trả về không được sắp xếp và không theo bất kỳ thứ tự cụ thể nào. Phương thức này trả về một mảng có độ dài 0 nếu lớp hoặc giao diện tuyên bố không có trường hoặc nếu đối tượng Class này đại diện cho một kiểu nguyên thủy, một lớp mảng hoặc void.


Và nếu tôi cần tất cả các trường từ tất cả các lớp cha thì sao? Một số mã là cần thiết, ví dụ: từ https://stackoverflow.com/a353103361/755804 :

public static List<Field> getAllModelFields(Class aClass) {
    List<Field> fields = new ArrayList<>();
    do {
        Collections.addAll(fields, aClass.getDeclaredFields());
        aClass = aClass.getSuperclass();
    } while (aClass != null);
    return fields;
}

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.