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類的遍歷。