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


109

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


28
「プライベート継承フィールド」は存在しません。フィールドがプライベートの場合、継承されず、親クラスのスコープにのみ残ります。親プライベートフィールドにアクセスするには、最初に親クラスにアクセスする必要があります(aioobeの応答を参照)
Benoit Courtine

6
つまり、保護フィールドは継承されますが、リフレクションによってそれらを取得するには、同じことを行う必要があります。
Bozho 2010

回答:


128

これはそれを解決する方法を示すはずです:

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

(またはClass.getDeclaredFields、すべてのフィールドの配列の場合。)

出力:

5

これはすべてのスーパークラスのフィールドを取得しますか、それとも直接のスーパークラスを取得しますか?

スーパークラスのフィールドを指示します。もっと上に行きたい場合は、getSuperclass()到達nullするまで再帰できます。
aioobe

なぜあなたは使用していないgetDeclaredFields()[0]getDeclaredField("i")ではなく、繰り返し[0]、次の二つの文で配列アクセスを?
Holger

これは、この特定の質問が定式化されている方法によるものです。基本的には、使い方のデモンストレーションでしたgetDeclaredFields。回答を更新しました。
aioobe

44

ここでの最善のアプローチは、ビジターパターンを使用して、クラスとすべてのスーパークラスのすべてのフィールドを検索し、それらに対してコールバックアクションを実行することです。


実装

Springには、それを実行する優れた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一時ブール値javax.management.relation.RoleUnresolvedList.typeSafeが見つかりましたタイプクラスjavax.management.relation.RoleUnresolvedListでフィールドプライベート一時ブール値javax.management.relation.RoleUnresolvedList.taintedが
見つかりました型クラスjava.util.ArrayList
Foundフィールドのプライベートトランジェントjava.lang.Object [] java.util.ArrayList.elementData型クラスjava.util.ArrayListのプライベートint java.util.ArrayList.size
フィールドprotectedトランジェントint Java。タイプクラスjava.util.AbstractListのutil.AbstractList.modCount


3
これは「訪問者パターン」ではありませんが、コードにSpringウイルスが含まれている場合は、非常に優れたツールです。それを共有してくれてありがとう:)
thinlizzy 2013

2
@ jose.diego私はあなたがそれについて議論することができると確信しています。オブジェクトツリーではなくクラス階層にアクセスしますが、原則は同じです
ショーンパトリックフロイド

このコメントが応答を得るかどうかはわかりませんが、この解決策では一度に特定のフィールドにのみアクセスします。同時に他のフィールドを確認する必要がある場合(たとえば、別のフィールドがNULLの場合にこのフィールドを "abc"に設定します)、オブジェクト全体を使用できません。
遺伝子b。

このクラスのJavaDocが「内部での使用のみを目的としている」ことを示しているのは残念です。そのため、これを使用することを望む人にとって、これはリスクの可能性があります。
宇宙飛行士のスピフ2018

1
@spacemanspiffあなたは技術的に正しいですが、このクラスは約15年間(4つのメジャーリリースバージョンを含む)存在し、多くのSpringの顧客によって広く使用されています。彼らが今それを取り戻すとは思えない。
ショーンパトリックフロイド

34

これでできます:

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の場合、これらのフィールドは合成とマークされ、次のようにフィルターで除外できます。

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

合成フィールドについてのご意見をお寄せいただきありがとうございます。EMMAでも同じことができます。
アナトリー

これは、引数クラスの宣言および継承されたフィールドを取得するため、getDeclaredAndInheritedPrivateFieldsという名前にする必要があります。おかげで完璧!
Peter Hawkins、

1
isSyntheticで素晴らしいキャッチ:)
Lucas Crawford

〜すばらしい答えてくれてありがとう

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

この回答に基づく)


15

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

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

しかし、彼の解決策はあなたを正しい道に導きましたね?
aperkins 2010

1
ベクトルは古いコードです。コレクションフレームワークからの現在のデータ構造を使用してください(ほとんどの場合はArrayListで十分です)
Sean Patrick Floyd

@aperkins aioobeの答えは私のように見えますが、私はそれを見つけて、その答えを見ました。@seanizer Vectorはそれほど古いものではなく、コレクションAPIのメンバーでもあります
ベンゼン

「Java 2プラットフォームv1.2以降、このクラスはListを実装するように改良されたため、Javaのコレクションフレームワークの一部になります。」1.2に後付け?それが古くないとしたら何ですか?ソース:download.oracle.com/javase/1.4.2/docs/api/java/util/Vector.html
Sean Patrick Floyd

7
すべてが同期されるため、Vectorには大きなオーバーヘッドがあります。また、同期が必要な場合は、java.util.concurrentに適切なクラスがあります。Vector、Hashtable、StringBufferは、ほとんどの場合、ArrayList、HashMap、StringBuilderで置き換える必要があります
Sean Patrick Floyd

8

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

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);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.