利用Iterator模式遍历JavaBean中的属性

缘起 (Motivation/intent)

JavaScript中可以遍历对象中的属性,但Java却没有这样的语言支持。例如一个普通POJO对象UserBean

public class UserBean {

    private int id;

    private String name;

    private Date birthdate;

 

   // getters & setters

}


现在想遍历对象中每个属性,获得如下的效果

UserBean user = new UserBean(1234, "张三", "1982-12-13");

for(Object propertyValue : user) {

     System.out.println(propertyValue);

}


将显示

1234

张三

1982-12-13


解决方案 (Solution)

使用Iterator模式。要使某个对象可以进行遍历循环操作,亦即可以适用于foreach,该对象必须实现Iterable接口。这个接口将会强制实现iterator()方法。

public class UserBean implements Iterable {

...

@Override

public Iterator iterator() {}

} 
  

实现Iterator的步骤如下

1. 在iterator()方法中,实例化我们自己的Iterator实现类,这里称之为MyObjectPropertyIterator

2. 要获取任何JavaBean对象的信息,既可以利用反射,也可以利用java.beans.Introspector这个工具类来获取一个BeanInfo对象

    2.1.1  BeanInfo beanInfo = Introspector.getBeanInfo(user.getClass());

    2.1.2  从BeanInfo对象中,我们可以获得一个PropertyDescriptor数组,其中就包含了bean对象中所有属性信息

           PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

    2.1.3  然后用PropertyDescription的readMethod方法获得其getter和setter,最后调用invoke方法得到返回结果

            for(PropertyDescriptor propertyDescriptor : propertyDescriptors)

                Object propertyValue = propertyDescriptor.getReadMethod().invoke(targetObject);

               System.out.println(propertyValue);

            }

    // 与我们的遍历对象属性无关没有实现的必要
    @Override
    public void remove() {}



   如果使用反射,则代码如下:

   2.2.1  Field[] fields = user.getClass().getDeclaredFields();

   2.2.2 遍历Field[]数组

           for(Field field : fields) {

               field.setAccessible(true); // 这句使我们可以访问似有成员变量

               Object property = field.get(user);

           } 

3. 利用步骤3的思路,创建我们的Iterator实现

public class PropertyIterator implements Iterator{
    private int index;
    private Object targetObject;
    PropertyDescriptor[] propertyDescriptors;
   

    // 通过构造器传入所要遍历的对象,即UserBean的对象
    PropertyIterator(Object targetObject) {
        this.targetObject = targetObject;
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(targetObject.getClass());
            propertyDescriptors = beanInfo.getPropertyDescriptors();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public boolean hasNext() {
        return this.index < this.propertyDescriptors.length;
    }

    @Override
    public Object next() {
        Object propertyValue = null;
        try {
            PropertyDescriptor propertyDescriptor = propertyDescriptors[index++];
            propertyValue= propertyDescriptor.getReadMethod().invoke(targetObject);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return propertyValue;
    }

//如果用反射

public class PropertyIterator implements Iterable {
    private final Object targetObject;
    
    public PropertyIterator(final Object targetObject) {
        this.targetObject = targetObject;
    }
    
    public Iterator iterator() {
        return new PropertyIteratorImpl();
    }
    private class PropertyIteratorImpl implements Iterator{
        int index;
        Field[] fields;

        PropertyIteratorImpl() {
            try {
                fields = targetObject.getClass().getDeclaredFields();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public boolean hasNext() {
            return this.index < this.fields.length;
        }

        @Override
        public Object next() {
            Object obj = null;
            try {
                Field field = fields[index++];
                field.setAccessible(true);
                obj = field.get(targetObject);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return obj;
        }

        @Override
        public void remove() {}
    }
}

 
  
4. 在UserBean的iterator方法中,返回MyPropertyIterator的实例化对象。 
    @Override

    public Iterator iterator() {

        return new  MyPropertyIterator(this);

    }

 
  

说明 (Note)

为了简明,没有说明Iterator设计模式的实现原理,关于这部分,秦参考有关设计模式的书籍,特别是GoF。或可参考java.util.LinkedList API的源代码。


评估 (Assessment)

对目标对象完全没有侵入性,将遍历功能与目标对象本身分离。上一个版本,让UserBean对象直接实现Itarable,虽然更直观,代码量也少,但通用型不强,有侵入性。如果不是自己创建的类,无法使用。这个版本将Iterator类的实现分离成单独一个类,使之更通用化。现在的版本已经不需要Bean对象实现Iterable接口,使得UserBean类更干净、纯粹,不牵涉和UserBeen业务无关的任何接口或抽象类,符合将变化点分离的OO设计思想。

另外,BeanInfo版本和反射版本有细微差别:BeanInfo的PropertyDescriptor通过getter读取类变量,而反射则直接读取似有成员变量。后者显得更暴力些。虽然从代码编写者的角度似乎更方便,但破坏了OO的封装和信息隐藏原则。


本篇笔记的目的是灵活使用Iterator模式。和大多数书本或网上教程不同,本篇利用Iterator模式实现了一个非Collection类的遍历。


你可能感兴趣的:(利用Iterator模式遍历JavaBean中的属性)