JavaScript中能够遍历对象中的属性,但Java却没有这样的语言支持。例如一个普通POJO对象UserBeanjava
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); }
将显示数组
1234ide
张三工具
1982-12-13this
使用Iterator模式。要使某个对象能够进行遍历循环操做,亦便可以适用于foreach,该对象必须实现Iterable接口。这个接口将会强制实现iterator()方法。spa
public class UserBean implements Iterable<Object> { ... @Override public Iterator<Object> iterator() {} }
实现Iterator的步骤以下设计
// 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); }
public class MyPropertyIterator implements Iterator<Object>{ 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<Object> { private final Object targetObject; public PropertyIterator(final Object targetObject) { this.targetObject = targetObject; } public Iterator<Object> iterator() { return new PropertyIteratorImpl(); } private class PropertyIteratorImpl implements Iterator<Object>{ 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() {} } }
@Override public Iterator<Object> iterator() { return new MyPropertyIterator(this); }
为了简明,没有说明Iterator设计模式的实现原理,关于这部分,请参考有关设计模式的书籍,特别是GoF。或可参考java.util.LinkedList API的源代码。code
对目标对象彻底没有侵入性,将遍历功能与目标对象自己分离。上一个版本,让UserBean对象直接实现Itarable,虽然更直观,代码量也少,但通用型不强,有侵入性。若是不是本身建立的类,没法使用。这个版本将Iterator类的实现分离成单独一个类,使之更通用化。如今的版本已经不须要Bean对象实现Iterable接口,使得UserBean类更干净、纯粹,不牵涉和UserBeen业务无关的任何接口或抽象类,符合将变化点分离的OO设计思想。对象
另外,BeanInfo版本和反射版本有细微差异:BeanInfo的PropertyDescriptor经过getter读取类变量,而反射则直接读取似有成员变量。后者显得更暴力些。虽然从代码编写者的角度彷佛更方便,但破坏了OO的封装和信息隐藏原则。
本篇笔记的目的是灵活使用Iterator模式。和大多数书本或网上教程不一样,本篇利用Iterator模式实现了一个非Collection类的遍历。