Яка різниця між getFields та getDeclaredFields у відображенні Java


195

Я трохи заплутаний у різниці між getFieldsметодом та getDeclaredFieldsметодом при використанні відображення Java.

Я читаю, що getDeclaredFieldsнадає вам доступ до всіх полів класу, і getFieldsповертає лише загальнодоступні поля. Якщо це так, то чому б ви не завжди використовували getDeclaredFields?

Чи може хтось, будь ласка, детальніше розглянути це і пояснити різницю між двома методами, і коли / чому ви хочете використовувати один над іншим?


3
getFieldможе отримати поле, успадковане від суперкласу, але getDeclaredFieldне може. getDeclaredFieldобмежитися класом, на якому ви викликаєте функцію.
user2336315

@ user2336315 це правильно, проте getFieldне може отримати доступ до приватних членів
Madbreaks

Відповіді:


258

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походить від Гуави.

Оновлення

FYI, наведений вище код опублікований на GitHub в моєму проекті LibEx в ReflectionUtils .


8
Чудова відповідь, але слід зазначити, що приватні поля в суперкласах не можуть використовуватися екземплярами поточного класу для Field#getподібних методів. Іншими словами, такий підхід не дозволяє поточному класу отримати доступ до приватного інтерфейсу свого суперкласу, так само як і типова компіляція.
FThompson

4
@ Вулкан Правда, якщо код не написаний, щоб використовувати відображення для зміни області через setAccessibleі немає на місці менеджера безпеки
Джон Б

Незначна нітка повинна бути "(незалежно від доступності)" не "(незалежно від сфери застосування"). Усі поля мають однаковий обсяг, а саме тіло класу .
ішавіт

@yshavit Дякую Оновлено.
Джон Б

1
Це не було б. Оскільки до privateполів можна отримати доступ, лише через getDeclaredFieldsякий є специфічним для класу. Кожне поле (навіть з тим самим типом та назвою) буде окремим Fieldекземпляром.
Джон Б

7

Як уже згадувалося, 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);
}

незначна модифікація, щоб все-таки викинути помилку, якщо її взагалі не знайшлиtry try { field = clazz.getDeclaredField(name); } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); if(clazz==null){ throw e; } }
Свен Даенз

5

public Field[] getFields() throws SecurityException

Повертає масив, що містить об'єкти Field, що відображають усі доступні загальнодоступні поля класу або інтерфейсу, представлені цим об'єктом Class. Елементи в масиві, що повертається, не сортуються і не є в якомусь конкретному порядку. Цей метод повертає масив довжиною 0, якщо клас або інтерфейс не мають доступних загальнодоступних полів або якщо він представляє клас масиву, примітивний тип або недійсний.

Зокрема, якщо цей об’єкт Class представляє клас, цей метод повертає загальнодоступні поля цього класу та всі його суперкласи. Якщо цей об'єкт Class представляє інтерфейс, цей метод повертає поля цього інтерфейсу та всі його суперінтерфейси.

Поле неявної довжини для класу масиву не відображається цим методом. Код користувача повинен використовувати методи класу Array для маніпулювання масивами.


public Field[] getDeclaredFields() throws SecurityException

Повертає масив об'єктів Field, що відображають усі поля, оголошені класом або інтерфейсом, представленими цим об'єктом Class. Це включає загальнодоступний, захищений, стандартний (пакетний) доступ та приватні поля, але виключає успадковані поля. Елементи в масиві, що повертається, не сортуються і не є в якомусь конкретному порядку. Цей метод повертає масив довжиною 0, якщо клас або інтерфейс не оголошує поля, або якщо цей об'єкт Class представляє примітивний тип, клас масиву або недійсний.


А що, якщо мені потрібні всі поля з усіх батьківських класів? Потрібен деякий код, наприклад, з 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;
}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.