【问题标题】:How to loop over a Class attributes in Java?如何在Java中循环一个类属性?
【发布时间】:2011-03-21 00:34:35
【问题描述】:

如何动态循环java中的类属性。

例如:

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

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

这在 Java 中可行吗?

【问题讨论】:

    标签: java attributes loops


    【解决方案1】:

    Java 有 Reflection (java.reflection.*),但我建议研究一下 Apache Beanutils 之类的库,与直接使用反射相比,它会使处理过程变得更少麻烦。

    【讨论】:

      【解决方案2】:

      没有语言支持可以满足您的要求。

      您可以在运行时使用反射来反射地访问类型的成员(例如,使用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()
                  );
              }
          }
      }
      

      上面的sn-p使用反射检查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
      

      Effective Java 2nd Edition,第 53 条:首选接口而不是反射

      以下是本书的节选:

      给定一个Class对象,你可以获得ConstructorMethodField代表类的构造函数、方法和字段的实例。 [它们] 让您反思地操纵它们的底层对应物。然而,这种力量是有代价的:

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

      作为一项规则,对象不应在运行时在正常应用程序中进行反射访问。

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

      【讨论】:

      • 其实根据字段的值,我想写还是不写每个字段的值?
      • @Zakaria:是的,你可以通过反射来实现这一点,但取决于你想要做什么,有更好的设计。
      • 事实上,我有一个要从一个类生成的报告,根据我想要放置或不放置这些字段的类字段的值,我可以实现的最佳设计是什么。?跨度>
      • @Zakaria:然后尝试反射。 Field.get 可以用来反射性地读取字段的值。如果是private,您可以使用setAccessible 解决这个问题。是的,反射非常强大,它可以让您执行检查private 字段、覆盖final 字段、调用private 构造函数等操作。如果您在设计方面需要进一步帮助,您可能应该写一个新问题有更多的信息。也许你一开始就不需要这么多属性(例如使用enumList 等)。
      • 泛型在这里没有用。就做static void inspect(Class&lt;?&gt; klazz)
      【解决方案3】:

      直接访问字段在 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 如果我理解正确,您可以检查propertyDesc.getReadMethod().getDeclaringClass() != Object.class,或者您可以指定一个类以停止分析作为第二个参数,如getBeanInfo(MyBean.class, Object.class)
      【解决方案4】:

      如果你的类符合 JavaBeabs 规范,我同意 Jörn's answer,但如果不符合 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));
      
          });
      }
      

      但是这里有一个警告:这个类被标记为“仅供内部使用”,如果你问我这很遗憾

      【讨论】:

        【解决方案5】:

        遍历类字段并从对象获取值的简单方法:

         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 尝试更改正在迭代的属性/字段的值时,您可能会遇到修改异常。
        【解决方案6】:

        这是一个按字母顺序对属性进行排序并将它们连同它们的值一起打印出来的解决方案:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-04-28
          • 2013-12-24
          • 1970-01-01
          • 1970-01-01
          • 2017-07-02
          • 1970-01-01
          • 1970-01-01
          • 2020-05-18
          相关资源
          最近更新 更多