Як прокрутити атрибути класу в Java?


85

Як я можу динамічно перебирати атрибути класу в Java.

Наприклад, наприклад:

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

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

чи можливо це в Java?

Відповіді:


100

Немає мовної підтримки, щоб робити те, про що ви просите.

Ви можете відображати доступ до членів типу під час виконання, використовуючи відображення (наприклад, 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, пункт 53: Віддайте перевагу інтерфейсам, ніж відображенню

Ось уривки з книги:

Отримавши Classоб’єкт, ви можете отримати Constructor, Methodта Fieldекземпляри, що представляють конструктори, методи та поля класу. [Вони] дозволяють вам рефлексивно маніпулювати їхніми аналогами . Однак ця сила має свою ціну:

  • Ви втрачаєте всі переваги перевірки під час компіляції.
  • Код, необхідний для здійснення світловідбиваючого доступу, незграбний і багатослівний.
  • Продуктивність страждає.

Як правило, об'єкти не повинні отримувати відображувальний доступ у звичайних додатках під час виконання.

Є кілька складних додатків, які потребують роздумів. Приклади включають [... спеціально опущено ...]. Якщо у вас є якісь сумніви щодо того, чи потрапляє ваша заявка до однієї з цих категорій, можливо, це не так.


насправді, відповідно до значення полів, я хочу написати чи не написати значення кожного поля?
Zakaria

@Zakaria: так, ви можете досягти цього з відображенням, але залежно від того, що ви намагаєтесь зробити, є набагато кращі проекти.
полігенмастильні речовини

Насправді, у мене є звіт, який слід створити з класу, і залежно від значення полів класу, які я хочу розмістити чи ні, ці поля є найкращим дизайном, який я можу реалізувати.?
Zakaria

1
@Zakaria: продовжуй і спробуй роздуми тоді. Є те, Field.getщо ви можете використовувати для відображення значень поля. Якщо це так private, ви, можливо, зможете обійти це setAccessible. Так, відображення є дуже потужним, і воно дозволяє вам робити такі речі, як перевірка privateполів, перезапис finalполів, виклик privateконструкторів тощо. Якщо вам потрібна подальша допомога в аспекті дизайну, ви, мабуть, повинні написати нове запитання з набагато більше інформації. Можливо, вам спочатку не потрібні ці багато атрибутів (наприклад, використання enumабо Listтощо).
полігенмастильні речовини

1
дженерики тут не мають ніякої користі. just dostatic void inspect(Class<?> klazz)
user102008

45

Доступ до полів безпосередньо не дуже хороший стиль у Java. Я б запропонував створити методи getter і setter для полів вашого 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));

    });
}

Але ось попередження: клас позначений як "лише для внутрішнього використання", що шкода, якщо ви запитаєте мене


13

Простий спосіб перебору полів класу та отримання значень від об’єкта:

 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 Чи є спосіб встановити нове значення для атрибутів, повторюючи кожне поле?
hyperfkcb

@hyperfkcb Ви можете отримати виняток модифікації при спробі змінити значення властивостей / полів, які ви переглядаєте.
Рікі

6

Java має Reflection (java.reflection. *), Але я б радив заглянути в бібліотеку, як Apache Beanutils, це зробить процес набагато менш волохатим, ніж використання рефлексії безпосередньо.


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());
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.