Java Reflection을 사용하여 상속 된 속성 이름 / 값 검색


128

'ParentObj'에서 확장 된 Java 객체 'ChildObj'가 있습니다. 이제 Java 리플렉션 메커니즘을 사용하여 상속 된 속성을 포함하여 ChildObj의 모든 속성 이름과 값을 검색 할 수 있습니까?

Class.getFields 는 공용 속성의 배열을 제공 하고 Class.getDeclaredFields 는 모든 필드의 배열을 제공하지만 상속 된 필드 목록을 포함하는 것은 없습니다.

상속 된 속성도 검색 할 수있는 방법이 있습니까?

답변:


173

아니요, 직접 작성해야합니다. 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));
}

2
예. 그것에 대해 생각했다. 그러나 다른 방법이 있는지 확인하고 싶었습니다. 감사. :)
Veera 2016 년

7
가변 인수를 전달하고 반환하는 것은 훌륭한 디자인이 아닙니다. fields.addAll (type.getDeclaredFields ()); add가있는 향상된 for 루프보다 더 일반적입니다.
Tom Hawtin-tackline

적어도 스택 오버플로에서 컴파일해야하며 약간 Arrays.asList를 추가해야한다고 생각합니다.
Tom Hawtin-tackline

코드가 상속되지 않은 개인 및 정적 필드와 모든 필드를 수집하는 것 같습니다.
피터 Verhas 19시

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

9
이것은 내가 선호하는 솔루션이지만 주어진 클래스의 필드도 반환하기 때문에 "getAllFields"라고 부릅니다.
Pino

3
나는 매우 재귀를 좋아하지만 (재미있다!)이 방법의 가독성과보다 직관적 인 매개 변수 (새 컬렉션이 필요하지 않음), 더 이상 if (for 절에 암시 적) 및 필드에 대한 반복이없는 것을 선호합니다. 그들 자신.
레미 모린

그것은 재귀가 불필요하다는 것을 보여줍니다. 그리고 나는 짧은 코드를 좋아합니다! 고마워! :)
Aquarius Power

수년 동안 나는 항상 초기 값이 정수라고 생각합니다. @ Veera의 질문으로 재귀 만 해결할 수 있다고 생각합니다. @ Esko Luontola 당신의 명령은 훌륭합니다.
Touya Akira

@ Esko : 정말 감사합니다. 하루를 구했다! 간결하고 완벽하게 작동합니다!
gaurav

37

대신 라이브러리를 사용하여이 작업을 수행하려는 경우 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));
    }
}

6
팔! 나는 바퀴를 재발 명하지 않는 것을 좋아합니다. 이것을 위해 건배.
Joshua Pinter

6

전화해야합니다 :

Class.getSuperclass().getDeclaredFields()

필요에 따라 상속 계층을 되풀이합니다.


5

반사 라이브러리 사용 :

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

4

재귀 솔루션은 괜찮습니다. 유일한 작은 문제는 선언되고 상속 된 멤버의 상위 집합을 반환한다는 것입니다. 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);
    }
}

2
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

스프링 유틸리티 라이브러리를 사용하면 하나의 특정 속성이 클래스에 존재하는지 확인할 수 있습니다.

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

@건배


1

당신은 시도 할 수 있습니다:

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

1

더 짧고 적은 객체 인스턴스화? ^^

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

HI @Alexis LEGROS : ArrayUtils가 기호를 찾을 수 없습니다.
Touya Akira

1
이 클래스는 Apache Commons Lang의 클래스입니다.
Alexis LEGROS

Apache에는이 질문 요청을 처리하기위한 FieldUtils.getAllFields 함수가 이미 있습니다.
Touya Akira

1

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

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

0

이것은 @ 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

FieldUtils에서 다루지 않는 몇 가지 단점이 있습니다. 특히 합성 필드 (예 : 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

    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.