web-dev-qa-db-ja.com

Java Reflectionを使用して、継承された属性の名前/値を取得する

私はJava 'ParentObj'から拡張されたオブジェクト 'ChildObj'を持っています。今、継承された属性を含むChildObjのすべての属性名と値を取得できる場合Javaリフレクションメカニズム?

Class.getFields は公開属性の配列を提供し、 Class.getDeclaredFields はすべてのフィールドの配列を提供しますが、継承されたフィールドのリストを含むものはありません。

継承された属性も取得する方法はありますか?

113
Veera

いいえ、自分で作成する必要があります。これは Class.getSuperClass() で呼び出される単純な再帰メソッドです。

public static List<Field> getAllFields(List<Field> fields, Class<?> type) {
    fields.addAll(Arrays.asList(type.getDeclaredFields()));

    if (type.getSuperclass() != null) {
        getAllFields(fields, type.getSuperclass());
    }

    return fields;
}

@Test
public void getLinkedListFields() {
    System.out.println(getAllFields(new LinkedList<Field>(), LinkedList.class));
}
160
dfa
    public static List<Field> getAllFields(Class<?> type) {
        List<Field> fields = new ArrayList<Field>();
        for (Class<?> c = type; c != null; c = c.getSuperclass()) {
            fields.addAll(Arrays.asList(c.getDeclaredFields()));
        }
        return fields;
    }
81
Esko Luontola

代わりにこれを達成するためにライブラリに依存したい場合、 Apache Commons Lang バージョン3.2+は FieldUtils.getAllFieldsList

import Java.lang.reflect.Field;
import Java.util.AbstractCollection;
import Java.util.AbstractList;
import Java.util.AbstractSequentialList;
import Java.util.Arrays;
import Java.util.LinkedList;
import Java.util.List;

import org.Apache.commons.lang3.reflect.FieldUtils;
import org.junit.Assert;
import org.junit.Test;

public class FieldUtilsTest {

    @Test
    public void testGetAllFieldsList() {

        // Get all fields in this class and all of its parents
        final List<Field> allFields = FieldUtils.getAllFieldsList(LinkedList.class);

        // Get the fields form each individual class in the type's hierarchy
        final List<Field> allFieldsClass = Arrays.asList(LinkedList.class.getFields());
        final List<Field> allFieldsParent = Arrays.asList(AbstractSequentialList.class.getFields());
        final List<Field> allFieldsParentsParent = Arrays.asList(AbstractList.class.getFields());
        final List<Field> allFieldsParentsParentsParent = Arrays.asList(AbstractCollection.class.getFields());

        // Test that `getAllFieldsList` did truly get all of the fields of the the class and all its parents 
        Assert.assertTrue(allFields.containsAll(allFieldsClass));
        Assert.assertTrue(allFields.containsAll(allFieldsParent));
        Assert.assertTrue(allFields.containsAll(allFieldsParentsParent));
        Assert.assertTrue(allFields.containsAll(allFieldsParentsParentsParent));
    }
}
33
Chris

呼び出す必要があります:

Class.getSuperclass().getDeclaredFields()

必要に応じて継承階層を再帰します。

6
Nick Holt

Reflectionsライブラリを使用:

public Set<Field> getAllFields(Class<?> aClass) {
    return org.reflections.ReflectionUtils.getAllFields(aClass);
}
5

再帰的な解決策は問題ありません。唯一の小さな問題は、宣言および継承されたメンバーのスーパーセットを返すことです。 getDeclaredFields()メソッドはプライベートメソッドも返すことに注意してください。したがって、スーパークラス階層全体をナビゲートすると、スーパークラスで宣言されたすべてのプライベートフィールドが含まれ、それらは継承されません。

Modifier.isPublicを使用した単純なフィルター|| Modifier.isProtected述語は次のことを行います。

import static Java.lang.reflect.Modifier.isPublic;
import static Java.lang.reflect.Modifier.isProtected;

(...)

List<Field> inheritableFields = new ArrayList<Field>();
for (Field field : type.getDeclaredFields()) {
    if (isProtected(field.getModifiers()) || isPublic(field.getModifiers())) {
       inheritableFields.add(field);
    }
}
3
Marek Dec
private static void addDeclaredAndInheritedFields(Class<?> c, Collection<Field> fields) {
    fields.addAll(Arrays.asList(c.getDeclaredFields())); 
    Class<?> superClass = c.getSuperclass(); 
    if (superClass != null) { 
        addDeclaredAndInheritedFields(superClass, fields); 
    }       
}

上記の「DidYouMeanThatTomHa ...」ソリューションの作業バージョン

2
Theo Platt

より短く、インスタンス化されるオブジェクトが少ない? ^^

private static Field[] getAllFields(Class<?> type) {
    if (type.getSuperclass() != null) {
        return (Field[]) ArrayUtils.addAll(getAllFields(type.getSuperclass()), type.getDeclaredFields());
    }
    return type.getDeclaredFields();
}
1
Alexis LEGROS

あなたが試すことができます:

   Class parentClass = getClass().getSuperclass();
   if (parentClass != null) {
      parentClass.getDeclaredFields();
   }
1
Manuel Selva

私は最近このコードを見ました org.Apache.commons.lang3.reflect.FieldUtils

public static List<Field> getAllFieldsList(final Class<?> cls) {
        Validate.isTrue(cls != null, "The class must not be null");
        final List<Field> allFields = new ArrayList<>();
        Class<?> currentClass = cls;
        while (currentClass != null) {
            final Field[] declaredFields = currentClass.getDeclaredFields();
            Collections.addAll(allFields, declaredFields);
            currentClass = currentClass.getSuperclass();
        }
        return allFields;
}
0
aman
private static void addDeclaredAndInheritedFields(Class c, Collection<Field> fields) {
    fields.addAll(Arrays.asList(c.getDeclaredFields()));
    Class superClass = c.getSuperclass();
    if (superClass != null) {
        addDeclaredAndInheritedFields(superClass, fields);
    }
}

これは、@ user1079877が受け入れた回答を言い換えたものです。関数のパラメーターを変更せず、いくつかの最新のJava=機能を使用するバージョンです。

public <T> Field[] getFields(final Class<T> type, final Field... fields) {
    final Field[] items = Stream.of(type.getDeclaredFields(), fields).flatMap(Stream::of).toArray(Field[]::new);
    if (type.getSuperclass() == null) {
        return items;
    } else {
        return getFields(type.getSuperclass(), items);
    }
}

この実装により、呼び出しがもう少し簡潔になります。

var fields = getFields(MyType.class);
0
scrutari