В чем разница между getFields и getDeclaredFields в отражении Java

Я немного озадачен разницей между getFields метод и getDeclaredFields метод при использовании отражения Java.

Я прочитал это getDeclaredFields дает вам доступ ко всем полям класса и getFields возвращает только открытые поля. Если это так, почему бы вам не всегда использовать getDeclaredFields?

Может ли кто-нибудь подробно рассказать об этом и объяснить разницу между этими двумя методами, и когда / почему вы хотели бы использовать один над другим?

3 ответа

Решение

GetFields()

Все public поля вверх по всей иерархии классов.

getDeclaredFields ()

Все поля, независимо от их доступности, но только для текущего класса, а не любых базовых классов, от которых может наследоваться текущий класс.

Чтобы получить все поля в иерархии, я написал следующую функцию:

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

exclusiveParent класс предназначен для предотвращения поиска полей из Object, Может быть null если вы действительно хотите Object поля.

Чтобы уточнить, Lists.newArrayList родом из гуавы.

Обновить

К вашему сведению, приведенный выше код опубликован на GitHub в моем проекте LibEx в ReflectionUtils.

Как уже упоминалось, Class.getDeclaredField(String) только смотрит на поля из Class в котором ты это называешь.

Если вы хотите найти Field в Class иерархия, вы можете использовать эту простую функцию:

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

Это полезно, чтобы найти private поле из суперкласса, например. Кроме того, если вы хотите изменить его значение, вы можете использовать его следующим образом:

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

public Field[] getFields() throws SecurityException

Возвращает массив, содержащий объекты Field, отражающие все доступные открытые поля класса или интерфейса, представленные этим объектом Class. Элементы в возвращаемом массиве не отсортированы и расположены не в определенном порядке. Этот метод возвращает массив длины 0, если у класса или интерфейса нет доступных открытых полей или если он представляет класс массива, примитивный тип или void.

В частности, если этот объект Class представляет класс, этот метод возвращает открытые поля этого класса и всех его суперклассов. Если этот объект Class представляет интерфейс, этот метод возвращает поля этого интерфейса и всех его суперинтерфейсов.

Поле неявной длины для класса массива не отражается этим методом. Пользовательский код должен использовать методы класса Array для манипулирования массивами.


public Field[] getDeclaredFields() throws SecurityException

Возвращает массив объектов Field, отражающих все поля, объявленные классом или интерфейсом, представленным этим объектом Class. Это включает в себя открытые, защищенные, доступ по умолчанию (пакет) и частные поля, но исключает унаследованные поля. Элементы в возвращаемом массиве не отсортированы и расположены не в определенном порядке. Этот метод возвращает массив длины 0, если класс или интерфейс не объявляет никаких полей или если этот объект Class представляет примитивный тип, класс массива или void.


А что если мне понадобятся все поля из всех родительских классов? Необходим некоторый код, например, с /questions/1837164/skopirujte-vse-znacheniya-iz-polej-odnogo-klassa-v-drugoj-s-pomoschyu-otrazheniya/1837198#1837198:

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;
}
Другие вопросы по тегам