如何在Java中遍历Class属性?


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有可能吗?

Answers:


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 2nd Edition,第53项:比较喜欢反射的接口

这些是本书摘录:

给定一个Class对象,您可以获取ConstructorMethodField代表该类的构造函数,方法和字段的实例。[它们]使您可以反思地操纵其下层对等物。但是,此功能需要付出代价:

  • 您将失去编译时检查的所有好处。
  • 执行反射访问所需的代码笨拙且冗长。
  • 性能受损。

通常,不应在运行时在普通应用程序中以反射方式访问对象。

有一些需要反射的复杂应用程序。示例包括[...故意省略...]如果您对您的应用程序是否属于以下类别之一有任何疑问,则可能不会。


其实,根据字段的值,我想写还是不写每个字段的值?
Zakaria

@Zakaria:是的,您可以通过反射来完成此操作,但是根据您要执行的操作,会有更好的设计。
polygenelubricants 2010年

实际上,我有一个要从类生成的报告,并且根据要放置或不放置这些字段的类字段的值,我可以实现的最佳设计是什么?
Zakaria 2010年

1
@Zakaria:继续尝试,然后尝试反思。还有Field.get,你可以用它来反射性地读取一个字段的值。如果是这样private,您也许可以通过解决该问题setAccessible。是的,反射功能非常强大,它可以让您执行诸如检查private字段,覆盖final字段,调用private构造函数等操作。如果您在设计方面需要进一步的帮助,则可能应该编写一个包含更多信息的新问题。也许您一开始不需要这么多属性(例如useenumList等)。
polygenelubricants 2010年

1
泛型在这里没有用。只是做static void inspect(Class<?> klazz)
user102008 2011年

45

在Java中,直接访问字段并不是真正好的样式。我建议为bean的字段创建getter和setter方法,然后使用java.beans包中的Introspector和BeanInfo类。

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

有什么办法只能获取bean的属性,而不能获取类的属性?即避免由getPropertyDescriptors返回的MyBean.class
hbprotoss

@hbprotoss如果我对您的理解正确,则可以进行检查propertyDesc.getReadMethod().getDeclaringClass() != Object.class,也可以指定一个类来停止分析,如作为第二个参数getBeanInfo(MyBean.class, Object.class)
约恩·霍斯特曼(JörnHorstmann)

20

如果您的课程符合JavaBeabs规范,虽然我同意Jörn的回答,但如果不符合JavaBeabs规范,这是一个不错的选择,并且您使用Spring。

Spring有一个名为ReflectionUtils的类,它提供了一些非常强大的功能,包括doWithFields(class,callback),这是一种访问者风格的方法,可让您使用这样的回调对象遍历类字段:

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

@Rickey是否有通过循环每个字段为属性设置新值的方法?
hyperfkcb

@hyperfkcb尝试更改要迭代的属性/字段的值时,可能会收到修改异常。
瑞奇


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());
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.