Quelle est la différence entre getFields et getDeclaredFields dans la réflexion Java

195

Je suis un peu confus sur la différence entre la getFieldsméthode et la getDeclaredFieldsméthode lors de l'utilisation de la réflexion Java.

J'ai lu que cela getDeclaredFieldsvous donne accès à tous les champs de la classe et qui getFieldsne renvoie que les champs publics. Si tel est le cas, pourquoi ne l'utiliseriez-vous pas toujours getDeclaredFields?

Quelqu'un peut-il expliquer cela et expliquer la différence entre les deux méthodes, et quand / pourquoi voudriez-vous utiliser l'une par rapport à l'autre?

NoirChapeauSamouraï
la source
3
getFieldpeut obtenir un champ hérité d'une superclasse mais getDeclaredFieldne le peut pas. getDeclaredFieldse limiter à la classe sur laquelle vous appelez la fonction.
user2336315
@ user2336315 c'est correct, mais getFieldne peut pas accéder aux membres privés
Madbreaks

Réponses:

258

getFields ()

Tous les publicchamps dans toute la hiérarchie des classes.

getDeclaredFields ()

Tous les champs, quelle que soit leur accessibilité, mais uniquement pour la classe actuelle, pas les classes de base dont la classe actuelle pourrait hériter.

Pour faire remonter tous les champs dans la hiérarchie, j'ai écrit la fonction suivante:

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 exclusiveParentclasse est fournie pour empêcher la récupération des champs à partir de Object. Il se peut nullque vous vouliez les Objectchamps.

Pour clarifier, Lists.newArrayListvient de la goyave.

Mettre à jour

Pour info, le code ci-dessus est publié sur GitHub dans mon projet LibEx dans ReflectionUtils .

Jean B
la source
8
Excellente réponse, mais il convient de noter que les champs privés dans les superclasses ne peuvent pas être utilisés par les instances de la classe actuelle pour Field#getet des méthodes similaires. En d'autres termes, cette approche ne permet pas à la classe actuelle d'accéder à l'interface privée de sa superclasse, de la même manière que la compilation typique ne le fait pas.
FThompson
4
@Vulcan True sauf si le code est écrit pour utiliser la réflexion pour modifier la portée via setAccessibleet qu'il n'y a pas de gestionnaire de sécurité en place
John B
Légère nit, devrait être "(peu importe l'accessibilité)" pas "(peu importe la portée)". Tous les champs ont la même portée, à savoir, le corps de la classe .
yshavit
@yshavit Merci. Actualisé.
John B
1
Ce ne serait pas le cas. Étant donné que les privatechamps ne sont accessibles que via getDeclaredFieldsqui est spécifique à la classe. Chaque champ (même avec le même type et le même nom) serait des Fieldinstances distinctes .
John B
7

Comme déjà mentionné, Class.getDeclaredField(String)ne regarde que les champs Classdans lesquels vous l'appelez.

Si vous souhaitez rechercher un Fielddans la Classhiérarchie, vous pouvez utiliser cette fonction 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;
}

Ceci est utile pour trouver un privatechamp d'une superclasse, par exemple. De plus, si vous souhaitez modifier sa valeur, vous pouvez l'utiliser comme ceci:

/**
 * 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
la source
légère modification pour toujours lancer une erreur si elle n'est pas du tout trouvéetry try { field = clazz.getDeclaredField(name); } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); if(clazz==null){ throw e; } }
Sven Dhaens
5

public Field[] getFields() throws SecurityException

Renvoie un tableau contenant des objets Field reflétant tous les champs publics accessibles de la classe ou de l'interface représentée par cet objet Class. Les éléments du tableau renvoyés ne sont pas triés et ne sont pas dans un ordre particulier. Cette méthode retourne un tableau de longueur 0 si la classe ou l'interface n'a pas de champs publics accessibles, ou si elle représente une classe de tableau, un type primitif ou void.

Plus précisément, si cet objet Class représente une classe, cette méthode retourne les champs publics de cette classe et de toutes ses superclasses. Si cet objet Class représente une interface, cette méthode retourne les champs de cette interface et de toutes ses super-interfaces.

Le champ de longueur implicite pour la classe de tableau n'est pas reflété par cette méthode. Le code utilisateur doit utiliser les méthodes de la classe Array pour manipuler les tableaux.


public Field[] getDeclaredFields() throws SecurityException

Renvoie un tableau d'objets Field reflétant tous les champs déclarés par la classe ou l'interface représentée par cet objet Class. Cela inclut les champs public, protégé, par défaut (package) et privé , mais exclut les champs hérités . Les éléments du tableau renvoyés ne sont pas triés et ne sont pas dans un ordre particulier. Cette méthode renvoie un tableau de longueur 0 si la classe ou l'interface ne déclare aucun champ, ou si cet objet Class représente un type primitif, une classe de tableau ou void.


Et si j'ai besoin de tous les champs de toutes les classes parentes? Un certain code est nécessaire, par exemple à partir 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
la source