web-dev-qa-db-ja.com

JavaリフレクションのgetFieldsとgetDeclaredFieldsの違いは何ですか

Javaリフレクションを使用するときのgetFieldsメソッドとgetDeclaredFieldsメソッドの違いについて少し混乱しています。

getDeclaredFieldsはクラスのすべてのフィールドにアクセスでき、getFieldsはパブリックフィールドのみを返すことを読みました。この場合、なぜgetDeclaredFieldsを常に使用しないのですか?

誰かがこれについて詳しく説明して、2つの方法の違いを説明し、いつ/なぜ他の方法を使用したいのですか?

159
BlackHatSamurai

getFields()

クラス階層全体のすべてのpublicフィールド。

getDeclaredFields()

アクセシビリティに関係なく、現在のクラスのみのすべてのフィールド。現在のクラスが継承する可能性のある基本クラスではありません。

階層のすべてのフィールドを取得するために、次の関数を作成しました。

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

exclusiveParentクラスは、Objectからのフィールドの取得を防ぐために提供されています。 nullフィールドが必要な場合は、Objectになります。

明確にするために、Lists.newArrayListはGuavaから来ています。

更新

参考までに、上記のコードは、GitHubの LibEx プロジェクトの ReflectionUtils で公開されています。

220
John B

既に述べたように、Class.getDeclaredField(String)は、呼び出したClassのフィールドのみを調べます。

Field階層内のClassを検索する場合は、次の単純な関数を使用できます。

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

これは、たとえばスーパークラスからprivateフィールドを見つけるのに便利です。また、値を変更する場合は、次のように使用できます。

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

public Field[] getFields() throws SecurityException

これによって表されるクラスまたはインターフェースのすべてのアクセス可能なパブリックフィールドを反映するFieldオブジェクトを含む配列を返しますクラスオブジェクト。返される配列内の要素はソートされておらず、特定の順序ではありません。このメソッドは、クラスまたはインターフェイスにアクセス可能なパブリックフィールドがない場合、または配列クラス、プリミティブ型、またはvoidを表す場合、長さ0の配列を返します。

具体的には、このClassオブジェクトがクラスを表す場合、このメソッドはこのクラスとそのすべてのスーパークラスのパブリックフィールドを返します。このClassオブジェクトがインターフェイスを表す場合、このメソッドはこのインターフェイスとそのすべてのスーパーインターフェイスのフィールドを返します。

配列クラスの暗黙の長さフィールドは、このメソッドには反映されません。ユーザーコードは、Arrayクラスのメソッドを使用して配列を操作する必要があります。


public Field[] getDeclaredFields() throws SecurityException

すべてのクラスによって宣言されたフィールドまたはこのクラスによって表されるインターフェースを反映するFieldオブジェクトの配列を返しますオブジェクト。このには、public、protected、default(package)アクセス、およびprivateフィールドが含まれますが、は、継承されたフィールドを除外します。返される配列内の要素はソートされておらず、特定の順序ではありません。このメソッドは、クラスまたはインターフェイスがフィールドを宣言しない場合、またはこのClassオブジェクトがプリミティブ型、配列クラス、またはvoidを表す場合、長さ0の配列を返します。


そして、すべての親クラスのすべてのフィールドが必要な場合はどうなりますか?いくつかのコードが必要です、例えばfrom https://stackoverflow.com/a/35103361/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;
}
0
Somnath Musib