Java의 리플렉션을 통해 개인 상속 필드에 액세스


109

상속 된 구성원을 통해 class.getDeclaredFields(); 개인 구성원에 액세스 하는 방법을 찾았 class.getFields() 지만 개인 상속 된 필드를 찾고 있습니다. 어떻게하면 되나요?


28
"상속 된 개인 필드"가 없습니다. 비공개 필드 인 경우 상속되지 않으며 상위 클래스의 범위에만 남아 있습니다. 부모 비공개 필드에 액세스하려면 먼저 부모 클래스에 액세스해야합니다 (참조 : aioobe의 응답)
Benoit Courtine

6
즉, 보호 된 필드는 상속되지만 반사를 통해 가져 오려면 동일한 작업을 수행해야합니다.
Bozho 2010-08-25

답변:


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

산출:

발견 된 필드 private transient boolean javax.management.relation.RoleUnresolvedList.typeSafe in type 클래스 javax.management.relation.RoleUnresolvedList
Found field private transient boolean javax.management.relation.RoleUnresolvedList.tainted in type class javax.management.relation.RoleUnresolvedList
Found field private transient java.lang.Object [] 클래스 java.util.ArrayList 유형의 java.util.ArrayList.elementData
발견 된 필드 private int 클래스 java.util.ArrayList 유형의 java.util.ArrayList.size
발견 된 필드 보호 된 transient int java. 클래스 java.util.AbstractList 유형의 util.AbstractList.modCount


3
이것은 "방문자 패턴"은 아니지만 코드에 Spring 바이러스가있는 경우 여전히 매우 좋은 도구입니다. 그것을 공유해 주셔서 감사합니다 :)
thinlizzy

2
@ jose.diego 나는 당신이 그것에 대해 논쟁 할 수 있다고 확신합니다. 객체 트리가 아닌 클래스 계층 구조를 방문하지만 원칙은 동일하게 유지됩니다
Sean Patrick Floyd

이 댓글이 응답을 받을지 확실하지 않지만이 솔루션을 사용하여 한 번에 특정 필드 만 방문하는 것입니다. 다른 필드를 동시에 볼 필요가있는 경우 (예 : 다른 필드가 NULL 인 경우이 필드를 "abc"로 설정)-객체 전체를 사용할 수 없습니다.
유전자 b.

이 클래스에 대한 JavaDoc이 "내부 사용만을위한 것"이라고 표시하는 것은 너무 나쁘기 때문에 사용하려는 모든 사람에게 위험 할 수 있습니다.
우주인 SPIFF

1
@spacemanspiff는 기술적으로 정확하지만이 클래스는 약 15 년 (4 개의 주요 릴리스 버전 포함)이었으며 많은 Spring 고객이 널리 사용했습니다. 나는 그들이 지금 그것을 되돌릴 것 같지 않다.
Sean Patrick Floyd

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도 마찬가지입니다.
Anatoliy

이것은 인수 클래스의 선언되고 상속 된 필드를 가져 오므로 이름은 getDeclaredAndInheritedPrivateFields 여야합니다. 감사합니다!
Peter Hawkins 2013 년

1
:)이 isSynthetic에 좋은 캐치
루카스 크로포드

훌륭한 답변 감사합니다 ~
Raining

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

1
벡터는 잘못된 오래된 코드입니다. 컬렉션 프레임 워크의 현재 데이터 구조를 사용하십시오 (대부분의 경우 ArrayList가 적합합니다)
Sean Patrick Floyd

@aperkins aioobe의 대답은 내 것처럼 보이지만 그것을 찾았고 대답을 보았습니다. @seanizer 벡터는 오래된하지 않고, it'a 컬렉션 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
벡터는 모든 것이 동기화되기 때문에 엄청난 오버 헤드가 있습니다. 그리고 동기화가 필요한 곳에 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.