同じ型どうしのオブジェクトのプロパティを比較する
オブジェクトのプロパティを比較して、異なるプロパティの値だけを取り出す処理が必要になったのですが、せっかくなので汎用的な形として実装してみました。
commons-lang と SpringFramework を使用しています。頑張れば標準ライブラリでもできるのでしょうが、面倒ですし本質的でない部分にコードの多くを費やさなければいけなくなりそうなので、避けました。なお、プロパティが equals を適切に実装していない場合は、上手く比較できないケースがあるかもしれません。
こういうときに BeanWrapper はとても役に立ちます。リフレクションまわりを扱うときは、DIコンテナで使われているクラスを上手く再利用するのが近道だと思っています。DIコンテナの実現にはリフレクションが必要ですが、生のままのリフレクションAPIは例外処理など脇道にそれるコードが多くなるので、ヘビィにリフレクションを使うならたいていは適切なラッパーが作られているものです。BeanWrapper も、そのようなラッパーのひとつである、といえるでしょう。
import java.beans.PropertyDescriptor; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.ObjectUtils; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; public class CompareUtil { public static <T> List<Difference> diff(T a, T b) { BeanWrapper aWrap = new BeanWrapperImpl(a); PropertyDescriptor[] pds = aWrap.getPropertyDescriptors(); List<String> propertyNames = new ArrayList<String>(); for (PropertyDescriptor pd : pds) { if (aWrap.isReadableProperty(pd.getName())) { propertyNames.add(pd.getName()); } } BeanWrapper bWrap = new BeanWrapperImpl(b); List<Difference> differences = new ArrayList<Difference>(); for (String propertyName : propertyNames) { Object aPropValue = aWrap.getPropertyValue(propertyName); Object bPropValue = bWrap.getPropertyValue(propertyName); if (!ObjectUtils.equals(aPropValue, bPropValue) ) { Difference d = new Difference(); d.setPropertyName(); d.setLeft(aPropValue); d.setRight(bPropValue); differences.add(d); } } return differenceItems; } public static class Difference { private String propertyName; private Object left; private Object right; // setter, getter は省略 } }