「ParentObj」から拡張されたJavaオブジェクト「ChildObj」を持っています。ここで、Javaリフレクションメカニズムを使用して、継承された属性も含めて、ChildObjのすべての属性名と値を取得できる場合はどうでしょうか。
Class.getFieldsはパブリック属性の配列を提供し、Class.getDeclaredFieldsはすべてのフィールドの配列を提供しますが、継承されたフィールドリストを含むフィールドはありません。
継承された属性を取得する方法もありますか?
「ParentObj」から拡張されたJavaオブジェクト「ChildObj」を持っています。ここで、Javaリフレクションメカニズムを使用して、継承された属性も含めて、ChildObjのすべての属性名と値を取得できる場合はどうでしょうか。
Class.getFieldsはパブリック属性の配列を提供し、Class.getDeclaredFieldsはすべてのフィールドの配列を提供しますが、継承されたフィールドリストを含むフィールドはありません。
継承された属性を取得する方法もありますか?
回答:
いいえ、自分で書く必要があります。これは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));
}
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;
}
これを実現するためにライブラリに依存したい場合は、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));
}
}
リフレクションライブラリを使用する:
public Set<Field> getAllFields(Class<?> aClass) {
return org.reflections.ReflectionUtils.getAllFields(aClass);
}
再帰的なソリューションは問題ありませんが、唯一の小さな問題は、宣言され継承されたメンバーのスーパーセットを返すことです。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);
}
}
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 ...」ソリューションの作業バージョン
Spring Utilライブラリを使用すると、特定の属性がクラスに存在するかどうかを確認するために使用できます。
Field field = ReflectionUtils.findRequiredField(YOUR_CLASS.class, "ATTRIBUTE_NAME");
log.info(field2.getName());
APIドキュメント:
https : //docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/util/ReflectionUtils.html
または
Field field2 = ReflectionUtils.findField(YOUR_CLASS.class, "ATTRIBUTE_NAME");
log.info(field2.getName());
APIドキュメント:
https : //docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/ReflectionUtils.html
@乾杯
より短く、インスタンス化されるオブジェクトが少ない?^^
private static Field[] getAllFields(Class<?> type) {
if (type.getSuperclass() != null) {
return (Field[]) ArrayUtils.addAll(getAllFields(type.getSuperclass()), type.getDeclaredFields());
}
return type.getDeclaredFields();
}
getFields():
クラス階層全体のすべてのパブリックフィールドを取得し、
getDeclaredFields():
現在のクラスの修飾子に関係なく、すべてのフィールドを取得します。したがって、関係するすべての階層を取得する必要があります。
私は最近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;
}
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);
FieldUtilsによって対処されない2つの癖があります。具体的には、合成フィールド(たとえば、JaCoCoによって挿入されます)、および列挙型のコースにはインスタンスごとにフィールドがあるという事実、およびオブジェクトグラフをトラバースしている場合、すべてのフィールドを取得してから、それぞれのフィールドを取得します。列挙型にアクセスすると、無限ループに入ります。拡張された解決策(そして正直に言うと、これはどこかのライブラリに住んでいる必要があると確信しています!):
/**
* Return a list containing all declared fields and all inherited fields for the given input
* (but avoiding any quirky enum fields and tool injected fields).
*/
public List<Field> getAllFields(Object input) {
return getFieldsAndInheritedFields(new ArrayList<>(), input.getClass());
}
private List<Field> getFieldsAndInheritedFields(List<Field> fields, Class<?> inputType) {
fields.addAll(getFilteredDeclaredFields(inputType));
return inputType.getSuperclass() == null ? fields : getFieldsAndInheritedFields(fields, inputType.getSuperclass());
}
/**
* Where the input is NOT an {@link Enum} type then get all declared fields except synthetic fields (ie instrumented
* additional fields). Where the input IS an {@link Enum} type then also skip the fields that are all the
* {@link Enum} instances as this would lead to an infinite loop if the user of this class is traversing
* an object graph.
*/
private List<Field> getFilteredDeclaredFields(Class<?> inputType) {
return Arrays.asList(inputType.getDeclaredFields()).stream()
.filter(field -> !isAnEnum(inputType) ||
(isAnEnum(inputType) && !isSameType(field, inputType)))
.filter(field -> !field.isSynthetic())
.collect(Collectors.toList());
}
private boolean isAnEnum(Class<?> type) {
return Enum.class.isAssignableFrom(type);
}
private boolean isSameType(Field input, Class<?> ownerType) {
return input.getType().equals(ownerType);
}
Spockのテストクラス(Groovyが合成フィールドを追加):
class ReflectionUtilsSpec extends Specification {
def "declared fields only"() {
given: "an instance of a class that does not inherit any fields"
def instance = new Superclass()
when: "all fields are requested"
def result = new ReflectionUtils().getAllFields(instance)
then: "the fields declared by that instance's class are returned"
result.size() == 1
result.findAll { it.name in ['superThing'] }.size() == 1
}
def "inherited fields"() {
given: "an instance of a class that inherits fields"
def instance = new Subclass()
when: "all fields are requested"
def result = new ReflectionUtils().getAllFields(instance)
then: "the fields declared by that instance's class and its superclasses are returned"
result.size() == 2
result.findAll { it.name in ['subThing', 'superThing'] }.size() == 2
}
def "no fields"() {
given: "an instance of a class with no declared or inherited fields"
def instance = new SuperDooperclass()
when: "all fields are requested"
def result = new ReflectionUtils().getAllFields(instance)
then: "the fields declared by that instance's class and its superclasses are returned"
result.size() == 0
}
def "enum"() {
given: "an instance of an enum"
def instance = Item.BIT
when: "all fields are requested"
def result = new ReflectionUtils().getAllFields(instance)
then: "the fields declared by that instance's class and its superclasses are returned"
result.size() == 3
result.findAll { it.name == 'smallerItem' }.size() == 1
}
private class SuperDooperclass {
}
private class Superclass extends SuperDooperclass {
private String superThing
}
private class Subclass extends Superclass {
private String subThing
}
private enum Item {
BIT("quark"), BOB("muon")
Item(String smallerItem) {
this.smallerItem = smallerItem
}
private String smallerItem
}
}