Quyền truy cập vào các trường kế thừa riêng tư thông qua phản chiếu trong Java


109

Tôi đã tìm thấy một cách để có được các thành viên kế thừa thông qua class.getDeclaredFields(); và gia nhập các thành viên riêng tư thông qua class.getFields() Nhưng tôi đang tìm kiếm các trường được kế thừa riêng tư. Làm thế nào tôi có thể đạt được điều này?


28
"trường thừa kế riêng" không tồn tại. Nếu một trường là private, nó sẽ không được kế thừa và chỉ nằm trong phạm vi của lớp cha. Để lĩnh vực tư nhân tiếp cận phụ huynh, bạn phải truy cập lớp cha mẹ đầu tiên (x aioobe của phản ứng)
Benoit Courtine

6
điều đó nói rằng, các trường được bảo vệ được kế thừa, nhưng bạn phải làm như vậy để lấy chúng bằng cách phản chiếu.
Bozho

Câu trả lời:


128

Điều này sẽ cho thấy cách giải quyết nó:

import java.lang.reflect.Field;

class Super {
    private int i = 5;
}

public class B extends Super {
    public static void main(String[] args) throws Exception {
        B b = new B();
        Field f = b.getClass().getSuperclass().getDeclaredField("i");
        f.setAccessible(true);
        System.out.println(f.get(b));
    }
}

(Hoặc Class.getDeclaredFieldscho một mảng của tất cả các trường.)

Đầu ra:

5

Điều này có nhận được tất cả các trường của lớp cha hay chỉ lớp cha trực tiếp?
Mưa

Các trường của siêu lớp trực tiếp. Bạn có thể lặp lại getSuperclass()cho đến khi đạt được nullnếu bạn muốn lên cao hơn.
aioobe

Tại sao bạn không sử dụng getDeclaredFields()[0]hoặc getDeclaredField("i")thay vào đó là lặp lại [0]quyền truy cập mảng trong hai câu lệnh tiếp theo?
Holger

Đó là do cách đặt câu hỏi cụ thể này. Về cơ bản nó chỉ là một minh chứng về cách sử dụng getDeclaredFields. Câu trả lời đã được cập nhật.
aioobe

44

Cách tiếp cận tốt nhất ở đây là sử dụng Mẫu khách truy cập để tìm tất cả các trường trong lớp và tất cả các siêu lớp và thực hiện hành động gọi lại trên chúng.


Thực hiện

Spring có một lớp Tiện ích tuyệt vời ReflectionUtilslàm được điều đó: nó định nghĩa một phương thức để lặp qua tất cả các trường của tất cả các siêu lớp với một lời gọi lại:ReflectionUtils.doWithFields()

Tài liệu:

Gọi lệnh gọi lại đã cho trên tất cả các trường trong lớp đích, đi lên cấu trúc phân cấp lớp để lấy tất cả các trường đã khai báo.

Các tham số:
- clazz - lớp đích cần phân tích
- fc - lệnh gọi lại để gọi cho mỗi trường
- ff - bộ lọc xác định các trường để áp dụng lệnh gọi lại

Mã mẫu:

ReflectionUtils.doWithFields(RoleUnresolvedList.class,
    new FieldCallback(){

        @Override
        public void doWith(final Field field) throws IllegalArgumentException,
            IllegalAccessException{

            System.out.println("Found field " + field + " in type "
                + field.getDeclaringClass());

        }
    },
    new FieldFilter(){

        @Override
        public boolean matches(final Field field){
            final int modifiers = field.getModifiers();
            // no static fields please
            return !Modifier.isStatic(modifiers);
        }
    });

Đầu ra:

Tìm thấy lĩnh vực thoáng qua tin boolean javax.management.relation.RoleUnresolvedList.typeSafe trong kiểu lớp javax.management.relation.RoleUnresolvedList
tìm thấy lĩnh vực thoáng qua tin boolean javax.management.relation.RoleUnresolvedList.tainted trong kiểu lớp javax.management.relation.RoleUnresolvedList
lĩnh vực tìm thấy private transient java.lang.Object [] java.util.ArrayList.elementData trong type class java.util.ArrayList
Found trường private int java.util.ArrayList.size trong type class java.util.ArrayList Trường
Found được bảo vệ thoáng qua int java. use.AbstractList.modCount trong loại lớp java.util.AbstractList


3
đó không phải là "kiểu khách truy cập", nhưng nó vẫn là một công cụ rất tốt nếu bạn có virus Spring trong mã của mình. nhờ để chia sẻ nó :)
thinlizzy

2
@ jose.diego Tôi khá chắc rằng bạn có thể tranh luận về điều đó. Nó đến thăm một hệ thống phân cấp lớp chứ không phải là một cây đối tượng, nhưng nguyên tắc vẫn giữ nguyên
Sean Patrick Floyd

Không chắc liệu nhận xét này có nhận được phản hồi hay không, nhưng bạn chỉ truy cập một trường cụ thể tại một thời điểm với giải pháp này. Nếu tôi cần xem xét các trường khác cùng lúc - ví dụ: đặt trường này thành "abc" nếu một trường khác là NULL - Tôi không có toàn bộ đối tượng.
gen b.

Thật tệ khi JavaDoc cho lớp này chỉ ra rằng "nó chỉ dành cho mục đích sử dụng nội bộ", vì vậy đây là rủi ro có thể xảy ra cho bất kỳ ai muốn sử dụng nó.
spiff spaceman

1
@spacemanspiff bạn nói đúng về mặt kỹ thuật, nhưng lớp này đã tồn tại khoảng 15 năm (bao gồm 4 phiên bản phát hành chính) và đã được nhiều khách hàng Spring sử dụng rộng rãi. Tôi nghi ngờ họ sẽ kéo nó trở lại bây giờ.
Sean Patrick Floyd

34

Điều này sẽ làm được:

private List<Field> getInheritedPrivateFields(Class<?> type) {
    List<Field> result = new ArrayList<Field>();

    Class<?> i = type;
    while (i != null && i != Object.class) {
        Collections.addAll(result, i.getDeclaredFields());
        i = i.getSuperclass();
    }

    return result;
}

Nếu bạn sử dụng một công cụ phủ mã như EclEmma , bạn phải chú ý: chúng thêm một trường ẩn vào mỗi lớp của bạn. Trong trường hợp của EclEmma, ​​các trường này được đánh dấu là tổng hợp và bạn có thể lọc chúng ra như sau:

private List<Field> getInheritedPrivateFields(Class<?> type) {
    List<Field> result = new ArrayList<Field>();

    Class<?> i = type;
    while (i != null && i != Object.class) {
        for (Field field : i.getDeclaredFields()) {
            if (!field.isSynthetic()) {
                result.add(field);
            }
        }
        i = i.getSuperclass();
    }

    return result;
}

Cảm ơn nhận xét của bạn về các trường tổng hợp, EMMA cũng làm như vậy.
Anatoliy

điều này được khai báo và kế thừa các trường của lớp đối số nên được đặt tên là getDeclaredAndInheritedPrivateFields. hoàn hảo mặc dù cảm ơn!
Peter Hawkins

1
bắt tốt về isSynthetic :)
Lucas Crawford

Cảm ơn vì câu trả lời tuyệt vời ~
Mưa

19
public static Field getField(Class<?> clazz, String fieldName) {
    Class<?> tmpClass = clazz;
    do {
        try {
            Field f = tmpClass.getDeclaredField(fieldName);
            return f;
        } catch (NoSuchFieldException e) {
            tmpClass = tmpClass.getSuperclass();
        }
    } while (tmpClass != null);

    throw new RuntimeException("Field '" + fieldName
            + "' not found on class " + clazz);
}

(dựa trên câu trả lời này )


15

Trên thực tế, tôi sử dụng hệ thống phân cấp kiểu phức tạp nên giải pháp của bạn không hoàn chỉnh. Tôi cần thực hiện một cuộc gọi đệ quy để nhận tất cả các trường được kế thừa riêng. Đây là giải pháp của tôi

 /**
 * Return the set of fields declared at all level of class hierachy
 */
public Vector<Field> getAllFields(Class clazz) {
    return getAllFieldsRec(clazz, new Vector<Field>());
}

private Vector<Field> getAllFieldsRec(Class clazz, Vector<Field> vector) {
    Class superClazz = clazz.getSuperclass();
    if(superClazz != null){
        getAllFieldsRec(superClazz, vector);
    }
    vector.addAll(toVector(clazz.getDeclaredFields()));
    return vector;
}

Tuy nhiên, giải pháp của anh ấy đã đưa bạn đi đúng đường, phải không?
aperkins

1
Vector là mã cũ xấu. Vui lòng sử dụng một cấu trúc dữ liệu hiện tại từ khuôn khổ bộ sưu tập (ArrayList là đủ trong hầu hết các trường hợp)
Sean Patrick Floyd

@aperkins câu trả lời của aioobe trông giống như của tôi, nhưng tôi đã tìm thấy nó và sau đó tôi thấy câu trả lời. @seanizer Vector không cũ như vậy và nó là một thành viên của API bộ sưu tập
benzen

"Đối với nền tảng Java 2 v1.2, lớp này đã được trang bị thêm để triển khai Danh sách, để nó trở thành một phần của khung thu thập của Java." trang bị thêm trong 1,2? nếu nó không cũ thì là gì? Nguồn: download.oracle.com/javase/1.4.2/docs/api/java/util/Vector.html
Sean Patrick Floyd

7
Vector có chi phí rất lớn vì mọi thứ đều được đồng bộ hóa. Và nơi bạn cần đồng bộ hóa, có các lớp tốt hơn trong java.util.concurrent. Vector, Hashtable và StringBuffer nên trong nhiều trường hợp được thay thế bởi ArrayList, HashMap và StringBuilder
Sean Patrick Floyd

8

Tôi cần thêm hỗ trợ cho các trường kế thừa cho các bản thiết kế trong Model Citizen . Tôi rút ra phương pháp này ngắn gọn hơn một chút để lấy các trường + trường kế thừa của Lớp '.

private List<Field> getAllFields(Class clazz) {
    List<Field> fields = new ArrayList<Field>();

    fields.addAll(Arrays.asList(clazz.getDeclaredFields()));

    Class superClazz = clazz.getSuperclass();
    if(superClazz != null){
        fields.addAll(getAllFields(superClazz));
    }

    return fields;
}

7
private static Field getField(Class<?> clazz, String fieldName) {
    Class<?> tmpClass = clazz;
    do {
        for ( Field field : tmpClass.getDeclaredFields() ) {
            String candidateName = field.getName();
            if ( ! candidateName.equals(fieldName) ) {
                continue;
            }
            field.setAccessible(true);
            return field;
        }
        tmpClass = tmpClass.getSuperclass();
    } while ( clazz != null );
    throw new RuntimeException("Field '" + fieldName +
        "' not found on class " + clazz);
}
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.