What is the best way to compare several javabean properties?

后端 未结 9 1810
礼貌的吻别
礼貌的吻别 2020-12-14 03:26

I need to compare dozens of fields in two objects (instances of the same class), and do some logging and updating in case there are differences. Meta code could look somethi

相关标签:
9条回答
  • 2020-12-14 04:01

    I would also propose a similar solution to the one by Alnitak.

    If the fields need to be iterated when comparing, why not dispense with the separate fields, and put the data into an array, a HashMap or something similar that is appropriate.

    Then you can access them programmatically, compare them etc. If different fields need to be treated & compared in different ways, you could create approriate helper classes for the values, which implement an interface.

    Then you could just do

    valueMap.get("myobject").compareAndChange(valueMap.get("myotherobject")
    

    or something along those lines...

    0 讨论(0)
  • 2020-12-14 04:03

    The JavaBeans API is intended to help with introspection. It has been around in one form or another since Java version 1.2 and has been pretty usable since version 1.4.

    Demo code that compares a list of properties in two beans:

      public static void compareBeans(PrintStream log,
          Object bean1, Object bean2, String... propertyNames)
          throws IntrospectionException,
          IllegalAccessException, InvocationTargetException {
        Set<String> names = new HashSet<String>(Arrays
            .asList(propertyNames));
        BeanInfo beanInfo = Introspector.getBeanInfo(bean1
            .getClass());
        for (PropertyDescriptor prop : beanInfo
            .getPropertyDescriptors()) {
          if (names.remove(prop.getName())) {
            Method getter = prop.getReadMethod();
            Object value1 = getter.invoke(bean1);
            Object value2 = getter.invoke(bean2);
            if (value1 == value2
                || (value1 != null && value1.equals(value2))) {
              continue;
            }
            log.format("%s: %s is different than %s%n", prop
                .getName(), "" + value1, "" + value2);
            Method setter = prop.getWriteMethod();
            setter.invoke(bean2, value2);
          }
        }
        if (names.size() > 0) {
          throw new IllegalArgumentException("" + names);
        }
      }
    

    Sample invocation:

    compareBeans(System.out, bean1, bean2, "foo", "bar");
    

    If you go the annotations route, consider dumping reflection and generating the comparison code with a compile-time annotation processor or some other code generator.

    0 讨论(0)
  • 2020-12-14 04:07

    since

    All fields are String type, and I can modify code of the class owning the fields if required.

    you could try this class:

    public class BigEntity {
    
        private final Map<String, String> data;
    
        public LongEntity() {
            data = new HashMap<String, String>();
        }
    
        public String getFIELD1() {
            return data.get(FIELD1);
        }
    
        public String getFIELD2() {
            return data.get(FIELD2);
        }
    
        /* blah blah */
        public void cloneAndLogDiffs(BigEntity other) {
            for (String field : fields) {
                String a = this.get(field);
                String b = other.get(field);
    
                if (!a.equals(b)) {
                    System.out.println("diff " + field);
                    other.set(field, this.get(field));
                }
            }
        }
    
        private String get(String field) {
            String value = data.get(field);
    
            if (value == null) {
                value = "";
            }
    
            return value;
        }
    
        private void set(String field, String value) {
            data.put(field, value);
        }
    
        @Override
        public String toString() {
            return data.toString();
        }
    

    magic code:

        private static final String FIELD1 = "field1";
        private static final String FIELD2 = "field2";
        private static final String FIELD3 = "field3";
        private static final String FIELD4 = "field4";
        private static final String FIELDN = "fieldN";
        private static final List<String> fields;
    
        static {
            fields = new LinkedList<String>();
    
            for (Field field : LongEntity.class.getDeclaredFields()) {
                if (field.getType() != String.class) {
                    continue;
                }
    
                if (!Modifier.isStatic(field.getModifiers())) {
                    continue;
                }
    
                fields.add(field.getName().toLowerCase());
            }
        }
    

    this class has several advantages:

    • reflects once, at class loading
    • it is very simply adding new fields, just add new static field (a better solution here is using Annotations: in the case you care using reflection works also java 1.4)
    • you could refactor this class in an abstract class, all derived class just get both
      data and cloneAndLogDiffs()
    • the external interface is typesafe (you could also easily impose immutability)
    • no setAccessible calls: this method is problematic sometimes
    0 讨论(0)
提交回复
热议问题