Как перебрать атрибуты класса в Java?

87

Как я могу динамически перебирать атрибуты класса в java.

Например:

public class MyClass{
    private type1 att1;
    private type2 att2;
    ...

    public void function(){
        for(var in MyClass.Attributes){
            System.out.println(var.class);
        }
    }
}

это возможно в Java?

Закария
источник

Ответы:

101

Нет лингвистической поддержки, чтобы сделать то, о чем вы просите.

Вы можете рефлексивно обращаться к членам типа во время выполнения, используя отражение (например, с помощью Class.getDeclaredFields()для получения массива Field), но в зависимости от того, что вы пытаетесь сделать, это может быть не лучшим решением.

Смотрите также

Связанные вопросы


пример

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

import java.lang.reflect.*;

public class DumpFields {
    public static void main(String[] args) {
        inspect(String.class);
    }
    static <T> void inspect(Class<T> klazz) {
        Field[] fields = klazz.getDeclaredFields();
        System.out.printf("%d fields:%n", fields.length);
        for (Field field : fields) {
            System.out.printf("%s %s %s%n",
                Modifier.toString(field.getModifiers()),
                field.getType().getSimpleName(),
                field.getName()
            );
        }
    }
}

В приведенном выше фрагменте кода используется отражение для проверки всех объявленных полей class String; он производит следующий вывод:

7 fields:
private final char[] value
private final int offset
private final int count
private int hash
private static final long serialVersionUID
private static final ObjectStreamField[] serialPersistentFields
public static final Comparator CASE_INSENSITIVE_ORDER

Эффективное Java 2-е издание, Правило 53: Предпочитайте интерфейсы отражению

Это отрывки из книги:

Для данного Classобъекта вы можете получить Constructor, Methodи Fieldэкземпляры, представляющие конструкторы, методы и поля класса. [Они] позволяют вам рефлексивно манипулировать своими основными собратьями . Однако эта сила имеет свою цену:

  • Вы теряете все преимущества проверки во время компиляции.
  • Код, необходимый для выполнения рефлексивного доступа, громоздок и многословен.
  • Страдает производительность.

Как правило, в обычных приложениях во время выполнения объекты не должны подвергаться рефлексивному доступу.

Есть несколько сложных приложений, требующих рефлексии. Примеры включают [... опущено намеренно ...] Если у вас есть какие-либо сомнения относительно того, попадает ли ваше приложение в одну из этих категорий, вероятно, нет.

полигенные смазочные материалы
источник
на самом деле, в зависимости от значения полей, я хочу писать или не писать значение каждого поля?
Zakaria
@Zakaria: да, вы можете добиться этого с помощью отражения, но в зависимости от того, что вы пытаетесь сделать, есть намного лучшие дизайны.
polygenelubricants
Фактически, у меня есть отчет, который нужно сгенерировать из класса, и в зависимости от значения полей класса, которые я хочу добавить, или нет, какой лучший дизайн я могу реализовать?
Zakaria
1
@Zakaria: давай, тогда попробуй поразмышлять. Это то, Field.getчто вы можете использовать для рефлексивного чтения значений поля. Если это так private, вы сможете обойти это с помощью setAccessible. Да, отражение - это очень мощный инструмент, который позволяет вам делать такие вещи, как проверка privateполей, перезапись finalполей, вызов privateконструкторов и т. Д. Если вам нужна дополнительная помощь с аспектом дизайна, вам, вероятно, следует написать новый вопрос с гораздо большей информацией. Возможно, вам вообще не нужны эти многие атрибуты (например, использование enumили Listи т. Д.).
polygenelubricants
1
дженерики здесь не нужны. static void inspect(Class<?> klazz)
Just
45

Прямой доступ к полям - не очень хороший стиль в java. Я бы предложил создать методы получения и установки для полей вашего bean-компонента, а затем использовать классы Introspector и BeanInfo из пакета java.beans.

MyBean bean = new MyBean();
BeanInfo beanInfo = Introspector.getBeanInfo(MyBean.class);
for (PropertyDescriptor propertyDesc : beanInfo.getPropertyDescriptors()) {
    String propertyName = propertyDesc.getName();
    Object value = propertyDesc.getReadMethod().invoke(bean);
}
Йорн Хорстманн
источник
Есть ли способ получить только свойства компонента, а не класса? т.е. избегайте MyBean.class, возвращаемого getPropertyDescriptors
hbprotoss
@hbprotoss, если я вас правильно понимаю, вы можете проверить propertyDesc.getReadMethod().getDeclaringClass() != Object.class, или вы можете указать класс для остановки анализа в качестве второго параметра, например getBeanInfo(MyBean.class, Object.class).
Йорн Хорстманн,
20

Хотя я согласен с ответом Йорна если ваш класс соответствует спецификации JavaBeabs, вот хорошая альтернатива, если это не так, и вы используете Spring.

Spring имеет класс ReflectionUtils, который предлагает очень мощную функциональность, включая doWithFields (класс, обратный вызов) , метод в стиле посетителя, который позволяет вам перебирать поля классов с помощью объекта обратного вызова, подобного этому:

public void analyze(Object obj){
    ReflectionUtils.doWithFields(obj.getClass(), field -> {

        System.out.println("Field name: " + field.getName());
        field.setAccessible(true);
        System.out.println("Field value: "+ field.get(obj));

    });
}

Но вот предупреждение: класс помечен как «только для внутреннего использования», что жаль, если вы спросите меня.

Шон Патрик Флойд
источник
14

Простой способ перебрать поля класса и получить значения из объекта:

 Class<?> c = obj.getClass();
 Field[] fields = c.getDeclaredFields();
 Map<String, Object> temp = new HashMap<String, Object>();

 for( Field field : fields ){
      try {
           temp.put(field.getName().toString(), field.get(obj));
      } catch (IllegalArgumentException e1) {
      } catch (IllegalAccessException e1) {
      }
 }
Рики
источник
Класс возможно или какой-то объект.
Rickey
@Rickey Есть ли способ установить новое значение для атрибутов, перебирая каждое поле в цикле?
hyperfkcb 02 янв.2020,
@hyperfkcb Вы можете получить исключение модификации при попытке изменить значения свойств / полей, которые вы повторяете.
Rickey
6

В Java есть Reflection (java.reflection. *), Но я бы посоветовал заглянуть в такую ​​библиотеку, как Apache Beanutils, это сделает процесс намного менее сложным, чем использование отражения напрямую.

Tassos Bassoukos
источник
0

Вот решение, которое сортирует свойства в алфавитном порядке и печатает их все вместе с их значениями:

public void logProperties() throws IllegalArgumentException, IllegalAccessException {
  Class<?> aClass = this.getClass();
  Field[] declaredFields = aClass.getDeclaredFields();
  Map<String, String> logEntries = new HashMap<>();

  for (Field field : declaredFields) {
    field.setAccessible(true);

    Object[] arguments = new Object[]{
      field.getName(),
      field.getType().getSimpleName(),
      String.valueOf(field.get(this))
    };

    String template = "- Property: {0} (Type: {1}, Value: {2})";
    String logMessage = System.getProperty("line.separator")
            + MessageFormat.format(template, arguments);

    logEntries.put(field.getName(), logMessage);
  }

  SortedSet<String> sortedLog = new TreeSet<>(logEntries.keySet());

  StringBuilder sb = new StringBuilder("Class properties:");

  Iterator<String> it = sortedLog.iterator();
  while (it.hasNext()) {
    String key = it.next();
    sb.append(logEntries.get(key));
  }

  System.out.println(sb.toString());
}
Бенни Нойгебауэр
источник