Acesso a campos herdados privados por meio da reflection em Java

Eu encontrei uma maneira de obter membros herdados via class.getDeclaredFields(); e class.getFields() membros privados via class.getFields() Mas estou procurando por campos herdados privados. Como posso conseguir isso?

Isso deve demonstrar como resolvê-lo:

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

Saída:

 5 

A melhor abordagem aqui é usar o Padrão de Visitante para encontrar todos os campos na class e todas as superclasss e executar uma ação de retorno de chamada nelas.


Implementação

Spring tem uma boa class de Utilitários ReflectionUtils que faz exatamente isso: define um método para fazer o loop de todos os campos de todas as superclasss com um callback: ReflectionUtils.doWithFields()

Documentação:

Invoque o retorno de chamada fornecido em todos os campos da class de destino, subindo na hierarquia de classs para obter todos os campos declarados.

parameters:
– clazz – a class alvo para analisar
– fc – o retorno de chamada para invocar para cada campo
– ff – o filtro que determina os campos para aplicar o retorno de chamada

Código de amostra:

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

Saída:

Encontrado campo privado transitório booleano javax.management.relation.RoleUnresolvedList.typeSafe na class de tipo javax.management.relation.RoleUnresolvedList
Encontrado campo privado transitório booleano javax.management.relation.RoleUnresolvedList.tainted na class de tipo javax.management.relation.RoleUnresolvedList
Encontrado campo privado transiente java.lang.Object [] java.util.ArrayList.elementData na class de tipo java.util.ArrayList
Campo encontrado private int java.util.ArrayList.size na class de tipo java.util.ArrayList
Encontrado campo protegido int transitivo java.util.AbstractList.modCount na class de tipo java.util.AbstractList

Isso vai fazer isso:

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

Se você usar uma ferramenta de cobertura de código como o EclEmma , terá que observar: eles adicionam um campo oculto a cada uma de suas classs. No caso do EclEmma, ​​esses campos são marcados como sintéticos e você pode filtrá-los assim:

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

(baseado nesta resposta)

Na verdade, eu uso uma hierarquia de tipo complexo para que sua solução não seja completa. Eu preciso fazer uma chamada recursiva para obter todos os campos herdados privados. Aqui está a minha solução

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

Eu precisava adicionar suporte para campos herdados de blueprints no Model Citizen . Eu deduzi esse método que é um pouco mais conciso para recuperar um campo de class + campos herdados.

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