¿Cuál es la diferencia entre getFields y getDeclaredFields en la reflexión de Java

194

Estoy un poco confundido acerca de la diferencia entre el getFieldsmétodo y el getDeclaredFieldsmétodo cuando uso la reflexión de Java.

Leí que getDeclaredFieldsle da acceso a todos los campos de la clase y que getFieldssolo devuelve campos públicos. Si este es el caso, ¿por qué no lo usarías siempre getDeclaredFields?

¿Alguien puede explicar esto y explicar la diferencia entre los dos métodos y cuándo / por qué querría usar uno sobre el otro?

BlackHatSamurai
fuente
3
getFieldpuede obtener un campo heredado de una superclase pero getDeclaredFieldno puede. getDeclaredFieldrestringirse a la clase en la que llama la función.
user2336315
@ user2336315 que es correcto, sin embargo getFieldno puede acceder a miembros privados
Madbreaks

Respuestas:

258

getFields ()

Todos los publiccampos ocupan toda la jerarquía de clases.

getDeclaredFields ()

Todos los campos, independientemente de su accesibilidad pero solo para la clase actual, no ninguna clase base de la que la clase actual podría estar heredando.

Para subir todos los campos a la jerarquía, he escrito la siguiente función:

public static Iterable<Field> getFieldsUpTo(@Nonnull Class<?> startClass, 
                                   @Nullable Class<?> exclusiveParent) {

   List<Field> currentClassFields = Lists.newArrayList(startClass.getDeclaredFields());
   Class<?> parentClass = startClass.getSuperclass();

   if (parentClass != null && 
          (exclusiveParent == null || !(parentClass.equals(exclusiveParent)))) {
     List<Field> parentClassFields = 
         (List<Field>) getFieldsUpTo(parentClass, exclusiveParent);
     currentClassFields.addAll(parentClassFields);
   }

   return currentClassFields;
}

La exclusiveParentclase se proporciona para evitar la recuperación de campos Object. Puede ser nullsi quieres los Objectcampos.

Para aclarar, Lists.newArrayListproviene de la guayaba.

Actualizar

Para su información, el código anterior se publica en GitHub en mi proyecto LibEx en ReflectionUtils .

John B
fuente
8
Gran respuesta, pero debe tenerse en cuenta que los campos privados en superclases no pueden ser utilizados por instancias de la clase actual para Field#getmétodos similares. En otras palabras, este enfoque no permite el acceso de la clase actual a la interfaz privada de su superclase, de la misma manera que la compilación típica no lo hace.
FThompson
44
@Vulcan True a menos que el código esté escrito para usar la reflexión para cambiar el alcance a través setAccessibley no haya un Administrador de seguridad en su lugar
John B
Lentitud, debe ser "(sin importar la accesibilidad)" no "(sin importar el alcance)". Todos los campos tienen el mismo alcance, es decir, el cuerpo de la clase .
yshavit
@yshavit Gracias. Actualizado.
John B
1
No debería. Dado privateque solo se puede acceder a los campos a través de los getDeclaredFieldscuales es específico de la clase. Cada campo (incluso con el mismo tipo y nombre) serían Fieldinstancias distintas .
John B
7

Como ya se mencionó, Class.getDeclaredField(String)solo mira los campos desde los Classque lo llama.

Si desea buscar Fielden la Classjerarquía, puede usar esta función simple:

/**
 * Returns the first {@link Field} in the hierarchy for the specified name
 */
public static Field getField(Class<?> clazz, String name) {
    Field field = null;
    while (clazz != null && field == null) {
        try {
            field = clazz.getDeclaredField(name);
        } catch (Exception e) {
        }
        clazz = clazz.getSuperclass();
    }
    return field;
}

Esto es útil para encontrar un privatecampo de una superclase, por ejemplo. Además, si desea modificar su valor, puede usarlo así:

/**
 * Sets {@code value} to the first {@link Field} in the {@code object} hierarchy, for the specified name
 */
public static void setField(Object object, String fieldName, Object value) throws Exception {
    Field field = getField(object.getClass(), fieldName);
    field.setAccessible(true);
    field.set(object, value);
}
IvanRF
fuente
ligera modificación para seguir arrojando error si no se encuentra en absolutotry try { field = clazz.getDeclaredField(name); } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); if(clazz==null){ throw e; } }
Sven Dhaens
5

public Field[] getFields() throws SecurityException

Devuelve una matriz que contiene objetos Field que reflejan todos los campos públicos accesibles de la clase o interfaz representada por este objeto Class. Los elementos en la matriz devuelta no están ordenados y no están en ningún orden en particular. Este método devuelve una matriz de longitud 0 si la clase o interfaz no tiene campos públicos accesibles, o si representa una clase de matriz, un tipo primitivo o nulo.

Específicamente, si este objeto de clase representa una clase, este método devuelve los campos públicos de esta clase y de todas sus superclases. Si este objeto de clase representa una interfaz, este método devuelve los campos de esta interfaz y de todas sus superinterfaces.

El campo de longitud implícita para la clase de matriz no se refleja en este método. El código de usuario debe usar los métodos de la clase Array para manipular las matrices.


public Field[] getDeclaredFields() throws SecurityException

Devuelve una matriz de objetos Field que reflejan todos los campos declarados por la clase o interfaz representada por este objeto Class. Esto incluye acceso público, protegido, predeterminado (paquete) y campos privados , pero excluye los campos heredados . Los elementos en la matriz devuelta no están ordenados y no están en ningún orden en particular. Este método devuelve una matriz de longitud 0 si la clase o interfaz declara que no hay campos, o si este objeto de clase representa un tipo primitivo, una clase de matriz o vacío.


¿Y si necesito todos los campos de todas las clases para padres? Se necesita algún código, por ejemplo, de https://stackoverflow.com/a/35103361/755804 :

public static List<Field> getAllModelFields(Class aClass) {
    List<Field> fields = new ArrayList<>();
    do {
        Collections.addAll(fields, aClass.getDeclaredFields());
        aClass = aClass.getSuperclass();
    } while (aClass != null);
    return fields;
}
18446744073709551615
fuente