web-dev-qa-db-ja.com

Javaのリフレクションを介したプライベート継承フィールドへのアクセス

class.getDeclaredFields();経由で継承メンバーを取得し、class.getFields()経由でプライベートメンバーにアクセスする方法を見つけましたが、プライベート継承フィールドを探しています。どうすればこれを達成できますか?

104
benzen

実際、私は複雑なタイプの階層を使用しているため、ソリューションは完全ではありません。すべてのプライベート継承フィールドを取得するには、再帰呼び出しを行う必要があります。ここに私の解決策があります

 /**
 * 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;
}
15
benzen

これはそれを解決する方法を示す必要があります:

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[] fs = b.getClass().getSuperclass().getDeclaredFields();
        fs[0].setAccessible(true);
        System.out.println(fs[0].get(b));
    }
}

出力:

5
123
aioobe

ここでの最良のアプローチは、 Visitor Pattern を使用して、クラスとすべてのスーパークラスのすべてのフィールドを見つけ、それらに対してコールバックアクションを実行することです。


実装

SpringにはNice Utilityクラスがあります ReflectionUtils それはまさにそれです:それはすべてのスーパークラスのすべてのフィールドをコールバックでループするメソッドを定義します: ReflectionUtils.doWithFields()

ドキュメンテーション:

ターゲットクラスのすべてのフィールドで指定されたコールバックを呼び出し、クラス階層を上ってすべての宣言されたフィールドを取得します。

パラメータ:
-clazz-分析するターゲットクラス
-fc-各フィールドに対して呼び出すコールバック
-ff-コールバックを適用するフィールドを決定するフィルター

サンプルコード:

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

出力:

型クラスjavax.management.relation.RoleUnresolvedListのフィールドprivate transient boolean javax.management.relation.RoleUnresolvedList.typeSafeが見つかりました
Foundフィールドprivate transient boolean javax.management.relation.RoleUnresolvedList.tainted in type class javax.management.relation.RoleUnresolvedList
フィールドprivate private Java.lang.Object []型クラスJava.util.ArrayListのJava.util.ArrayList.elementDataが見つかりました
型クラスJava.util.ArrayListにフィールドprivate int Java.util.ArrayList.sizeが見つかりました
タイプフィールドJava.util.AbstractListのフィールドprotected transient int Java.util.AbstractList.modCountが見つかりました

44

これはそれを行います:

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

EclEmma のようなコードカバレッジツールを使用する場合は、注意する必要があります。各クラスに非表示フィールドが追加されます。 EclEmmaの場合、これらのフィールドはsyntheticとマークされており、次のようにフィルタリングできます。

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

this answerに基づく)

19
Exterminator13

Model Citizen でブループリントの継承フィールドのサポートを追加する必要がありました。このメソッドは、クラスのフィールド+継承フィールドを取得するためにもう少し簡潔になりました。

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;
}
8
mguymon
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);
}
7
Kenny Cason