Acceso a campos privados heredados a través de la reflexión en Java

Encontré una manera de obtener miembros heredados a través de class.getDeclaredFields(); y acceso a miembros privados a través de class.getFields() Pero estoy buscando campos privados heredados. ¿Cómo puedo conseguir esto?

Esto debería demostrar cómo resolverlo:

 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[] fs = b.getClass().getSuperclass().getDeclaredFields(); fs[0].setAccessible(true); System.out.println(fs[0].get(b)); } } 

Salida:

 5 

El mejor enfoque aquí es usar el Patrón de visitante , encontrar todos los campos en la clase y todas las superclases y ejecutar una acción de callback sobre ellos.


Implementación

Spring tiene una buena clase de utilidad ReflectionUtils que hace precisamente eso: define un método para recorrer todos los campos de todas las súper clases con una callback: ReflectionUtils.doWithFields()

Documentación:

Invoca la callback dada en todos los campos de la clase objective, subiendo por la jerarquía de clases para obtener todos los campos declarados.

Parámetros:
– clazz – la clase objective para analizar
– fc – la callback a invocar para cada campo
– ff – el filtro que determina los campos para aplicar la callback a

Código de muestra:

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

Salida:

Campo encontrado private booleano transitorio javax.management.relation.RoleUnresolvedList.typeSafe en la clase de tipo javax.management.relation.RoleUnresolvedList
Campo encontrado privado booleano transitorio javax.management.relation.RoleUnresolvedList.tainted in tipo clase javax.management.relation.RoleUnresolvedList
Transitorio privado privado encontrado java.lang.Object [] java.util.ArrayList.elementData en la clase de tipo java.util.ArrayList
Campo encontrado private int java.util.ArrayList.size en la clase de tipo java.util.ArrayList
Transient protegido del campo encontrado int int java.util.AbstractList.modCount en la clase de tipo java.util.AbstractList

Esto lo hará:

 private List getInheritedPrivateFields(Class< ?> type) { List result = new ArrayList(); Class< ?> i = type; while (i != null && i != Object.class) { Collections.addAll(result, i.getDeclaredFields()); i = i.getSuperclass(); } return result; } 

Si usa una herramienta de cobertura de código como EclEmma , debe tener cuidado: agregan un campo oculto a cada una de sus clases. En el caso de EclEmma, ​​estos campos están marcados como sintéticos , y usted puede filtrarlos así:

 private List getInheritedPrivateFields(Class< ?> type) { List result = new ArrayList(); 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; } 
 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); } 

(basado en esta respuesta)

De hecho, utilizo una jerarquía de tipo complejo para que su solución no esté completa. Necesito hacer una llamada recursiva para obtener todos los campos privados heredados. Aquí está mi solución

  /** * Return the set of fields declared at all level of class hierachy */ public Vector getAllFields(Class clazz) { return getAllFieldsRec(clazz, new Vector()); } private Vector getAllFieldsRec(Class clazz, Vector vector) { Class superClazz = clazz.getSuperclass(); if(superClazz != null){ getAllFieldsRec(superClazz, vector); } vector.addAll(toVector(clazz.getDeclaredFields())); return vector; } 
 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); } 

Necesitaba agregar soporte para campos heredados para planos en Model Citizen . Obtuve este método que es un poco más conciso para recuperar campos de clase + campos heredados.

 private List getAllFields(Class clazz) { List fields = new ArrayList(); fields.addAll(Arrays.asList( clazz.getDeclaredFields() )); Class superClazz = clazz.getSuperclass(); if(superClazz != null){ fields.addAll( getAllFields(superClazz) ); } return fields; }