(二十五)Java工具类EqualsBuilder协助Object.equals(object)方法详解

时间:2022-11-20 16:16:59

原文链接:https://blog.csdn.net/yaomingyang/article/details/79300312

1.EqualsBuilder类基本简介

EqualsBuilder类提供方法为任何类建立良好的equals方法,它遵循Effective java定义的规则,特别是比较double、float、数组的大小是很棘手的,同时,确保equals()和hashcode()一致是很困难的。 
两个相等的对象必须生成相同的哈希代码,但是具有相同哈希代码的两个对象不一定相等。 
所有的相关字段都应该包含在相等的计算中,派生字段可能被忽略,特别是任何字段生成哈希码都必须在equals方法中,反之亦然; 
代码的典型用法如下:

 public boolean equals(Object obj) {
   if (obj == null) { return false; }
   if (obj == this) { return true; }
   if (obj.getClass() != getClass()) {
     return false;
   }
   MyClass rhs = (MyClass) obj;
   return new EqualsBuilder()
                 .appendSuper(super.equals(obj))
                 .append(field1, rhs.field1)
                 .append(field2, rhs.field2)
                 .append(field3, rhs.field3)
                 .isEquals();
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

另外,有一种方法使用反射来确定要测试的字段。因为这些字段通常是私有的,方法是reflectionEquals,使用AccessibleObject.setAccessible方法改变字段的可见性,这将在安全管理器下失败,除非正确设置适当的权限,它也比测试明显慢很多,非原始数据类型使用equals比较;

这种方法的典型调用如下:

 public boolean equals(Object obj) {
   return EqualsBuilder.reflectionEquals(this, obj);
 }
  • 1
  • 2
  • 3

2.EqualsBuilder类源码详解


 package org.apache.commons.lang3.builder;

 import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.tuple.Pair;
 /** * * 类描述 该类实现Builder<Boolean>接口,使用建造者模式增强equals方法 * * @param isEquals * */
 public class EqualsBuilder
   implements Builder<Boolean>
 {
   private static final ThreadLocal<Set<Pair<IDKey, IDKey>>> REGISTRY = new ThreadLocal();
   /** * * 方法描述 * * @param isEquals * */
   static Set<Pair<IDKey, IDKey>> getRegistry()
   {
     return (Set)REGISTRY.get();
   }
   /** * * 方法描述 * * @param isEquals * */
   static Pair<IDKey, IDKey> getRegisterPair(Object lhs, Object rhs)
   {
     IDKey left = new IDKey(lhs);
     IDKey right = new IDKey(rhs);
     return Pair.of(left, right);
   }

   /** * * 方法描述 * * @param isEquals * */
   static boolean isRegistered(Object lhs, Object rhs)
   {
     Set<Pair<IDKey, IDKey>> registry = getRegistry();
     Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
     Pair<IDKey, IDKey> swappedPair = Pair.of(pair.getRight(), pair.getLeft());

     return (registry != null) && ((registry.contains(pair)) || (registry.contains(swappedPair)));
   }

   /** * * 方法描述 * * @param isEquals * */
   private static void register(Object lhs, Object rhs)
   {
     Set<Pair<IDKey, IDKey>> registry = getRegistry();
     if (registry == null) {
       registry = new HashSet();
       REGISTRY.set(registry);
     }
     Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
     registry.add(pair);
   }

   /** * * 方法描述 * * @param isEquals * */
   private static void unregister(Object lhs, Object rhs)
   {
     Set<Pair<IDKey, IDKey>> registry = getRegistry();
     if (registry != null) {
       Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
       registry.remove(pair);
       if (registry.isEmpty()) {
         REGISTRY.remove();
       }
     }
   }

   private boolean isEquals = true;

   private boolean testTransients = false;
   private boolean testRecursive = false;
   private Class<?> reflectUpToClass = null;
   private String[] excludeFields = null;
   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder() {}

   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder setTestTransients(boolean testTransients)
   {
     this.testTransients = testTransients;
     return this;
   }
   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder setTestRecursive(boolean testRecursive)
   {
     this.testRecursive = testRecursive;
     return this;
   }
   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder setReflectUpToClass(Class<?> reflectUpToClass)
   {
     this.reflectUpToClass = reflectUpToClass;
     return this;
   }

   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder setExcludeFields(String... excludeFields)
   {
     this.excludeFields = excludeFields;
     return this;
   }
   /** * * 方法描述 * * @param isEquals * */
   public static boolean reflectionEquals(Object lhs, Object rhs, Collection<String> excludeFields)
   {
     return reflectionEquals(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
   }
   /** * * 方法描述 * * @param isEquals * */
   public static boolean reflectionEquals(Object lhs, Object rhs, String... excludeFields)
   {
     return reflectionEquals(lhs, rhs, false, null, excludeFields);
   }

   /** * * 方法描述 * * @param isEquals * */
   public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients)
   {
     return reflectionEquals(lhs, rhs, testTransients, null, new String[0]);
   }

   /** * * 方法描述 * * @param isEquals * */
   public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients, Class<?> reflectUpToClass, String... excludeFields)
   {
     return reflectionEquals(lhs, rhs, testTransients, reflectUpToClass, false, excludeFields);
   }
   /** * * 方法描述 * * @param isEquals * */
   public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients, Class<?> reflectUpToClass, boolean testRecursive, String... excludeFields)
   {
     if (lhs == rhs) {
       return true;
     }
     if ((lhs == null) || (rhs == null)) {
       return false;
     }
     return new EqualsBuilder().setExcludeFields(excludeFields).setReflectUpToClass(reflectUpToClass).setTestTransients(testTransients).setTestRecursive(testRecursive).reflectionAppend(lhs, rhs).isEquals();
   }
   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder reflectionAppend(Object lhs, Object rhs)
   {
     if (!this.isEquals) {
       return this;
     }
     if (lhs == rhs) {
       return this;
     }
     if ((lhs == null) || (rhs == null)) {
       this.isEquals = false;
       return this;
     }





     Class<?> lhsClass = lhs.getClass();
     Class<?> rhsClass = rhs.getClass();

     if (lhsClass.isInstance(rhs)) {
       Class<?> testClass = lhsClass;
       if (!rhsClass.isInstance(lhs))
       {
         testClass = rhsClass;
       }
     } else if (rhsClass.isInstance(lhs)) {
       Class<?> testClass = rhsClass;
       if (!lhsClass.isInstance(rhs))
       {
         testClass = lhsClass;
       }
     }
     else {
       this.isEquals = false;
       return this;
     }
     try {
       Class<?> testClass;
       if (testClass.isArray()) {
         append(lhs, rhs);
       } else {
         reflectionAppend(lhs, rhs, testClass);
         while ((testClass.getSuperclass() != null) && (testClass != this.reflectUpToClass)) {
           testClass = testClass.getSuperclass();
           reflectionAppend(lhs, rhs, testClass);
         }

       }


     }
     catch (IllegalArgumentException e)
     {
       this.isEquals = false;
       return this;
     }
     return this;
   }

   /** * * 方法描述 * * @param isEquals * */
   private void reflectionAppend(Object lhs, Object rhs, Class<?> clazz)
   {
     if (isRegistered(lhs, rhs)) {
       return;
     }
     try
     {
       register(lhs, rhs);
       Field[] fields = clazz.getDeclaredFields();
       AccessibleObject.setAccessible(fields, true);
       for (int i = 0; (i < fields.length) && (this.isEquals); i++) {
         Field f = fields[i];
         if ((!ArrayUtils.contains(this.excludeFields, f.getName())) && (!f.getName().contains("$")) && ((this.testTransients) || (!Modifier.isTransient(f.getModifiers()))) && (!Modifier.isStatic(f.getModifiers())) && (!f.isAnnotationPresent(EqualsExclude.class)))
         {

           try
           {

             append(f.get(lhs), f.get(rhs));
           }
           catch (IllegalAccessException e)
           {
             throw new InternalError("Unexpected IllegalAccessException");
           }
         }
       }
     } finally {
       unregister(lhs, rhs);
     }
   }

   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder appendSuper(boolean superEquals)
   {
     if (!this.isEquals) {
       return this;
     }
     this.isEquals = superEquals;
     return this;
   }

   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder append(Object lhs, Object rhs)
   {
     if (!this.isEquals) {
       return this;
     }
     if (lhs == rhs) {
       return this;
     }
     if ((lhs == null) || (rhs == null)) {
       setEquals(false);
       return this;
     }
     Class<?> lhsClass = lhs.getClass();
     if (!lhsClass.isArray())
     {
       if ((this.testRecursive) && (!ClassUtils.isPrimitiveOrWrapper(lhsClass))) {
         reflectionAppend(lhs, rhs);
       } else {
         this.isEquals = lhs.equals(rhs);
       }

     }
     else {
       appendArray(lhs, rhs);
     }
     return this;
   }

   /** * * 方法描述 * * @param isEquals * */
   private void appendArray(Object lhs, Object rhs)
   {
     if (lhs.getClass() != rhs.getClass()) {
       setEquals(false);
     } else if ((lhs instanceof long[])) {
       append((long[])lhs, (long[])rhs);
     } else if ((lhs instanceof int[])) {
       append((int[])lhs, (int[])rhs);
     } else if ((lhs instanceof short[])) {
       append((short[])lhs, (short[])rhs);
     } else if ((lhs instanceof char[])) {
       append((char[])lhs, (char[])rhs);
     } else if ((lhs instanceof byte[])) {
       append((byte[])lhs, (byte[])rhs);
     } else if ((lhs instanceof double[])) {
       append((double[])lhs, (double[])rhs);
     } else if ((lhs instanceof float[])) {
       append((float[])lhs, (float[])rhs);
     } else if ((lhs instanceof boolean[])) {
       append((boolean[])lhs, (boolean[])rhs);
     }
     else {
       append((Object[])lhs, (Object[])rhs);
     }
   }
   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder append(long lhs, long rhs)
   {
     if (!this.isEquals) {
       return this;
     }
     this.isEquals = (lhs == rhs);
     return this;
   }

   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder append(int lhs, int rhs)
   {
     if (!this.isEquals) {
       return this;
     }
     this.isEquals = (lhs == rhs);
     return this;
   }

   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder append(short lhs, short rhs)
   {
     if (!this.isEquals) {
       return this;
     }
     this.isEquals = (lhs == rhs);
     return this;
   }

   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder append(char lhs, char rhs)
   {
     if (!this.isEquals) {
       return this;
     }
     this.isEquals = (lhs == rhs);
     return this;
   }
   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder append(byte lhs, byte rhs)
   {
     if (!this.isEquals) {
       return this;
     }
     this.isEquals = (lhs == rhs);
     return this;
   }

   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder append(double lhs, double rhs)
   {
     if (!this.isEquals) {
       return this;
     }
     return append(Double.doubleToLongBits(lhs), Double.doubleToLongBits(rhs));
   }

   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder append(float lhs, float rhs)
   {
     if (!this.isEquals) {
       return this;
     }
     return append(Float.floatToIntBits(lhs), Float.floatToIntBits(rhs));
   }

   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder append(boolean lhs, boolean rhs)
   {
     if (!this.isEquals) {
       return this;
     }
     this.isEquals = (lhs == rhs);
     return this;
   }

   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder append(Object[] lhs, Object[] rhs)
   {
     if (!this.isEquals) {
       return this;
     }
     if (lhs == rhs) {
       return this;
     }
     if ((lhs == null) || (rhs == null)) {
       setEquals(false);
       return this;
     }
     if (lhs.length != rhs.length) {
       setEquals(false);
       return this;
     }
     for (int i = 0; (i < lhs.length) && (this.isEquals); i++) {
       append(lhs[i], rhs[i]);
     }
     return this;
   }
   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder append(long[] lhs, long[] rhs)
   {
     if (!this.isEquals) {
       return this;
     }
     if (lhs == rhs) {
       return this;
     }
     if ((lhs == null) || (rhs == null)) {
       setEquals(false);
       return this;
     }
     if (lhs.length != rhs.length) {
       setEquals(false);
       return this;
     }
     for (int i = 0; (i < lhs.length) && (this.isEquals); i++) {
       append(lhs[i], rhs[i]);
     }
     return this;
   }
   /** * * 方法描述 * * @param isEquals * */
   public EqualsBuilder append(int[] lhs, int[] rhs)
   {
     if (!this.isEquals) {
       return this;
     }
     if (lhs == rhs) {
       return this;
     }
     if ((lhs == null) || (rhs == null)) {
       setEquals(false);
       return this;
     }
     if (lhs.length != rhs.length) {
       setEquals(false);
       return this;
     }
     for (int i = 0; (i < lhs.length) && (this.isEquals); i++) {
       append(lhs[i], rhs[i]);
     }
     return this;
   }
   /** * * 方法描述 * * @param isEquals * */
  public EqualsBuilder append(short[] lhs, short[] rhs)
  {
    if (!this.isEquals) {
      return this;
    }
    if (lhs == rhs) {
      return this;
    }
    if ((lhs == null) || (rhs == null)) {
      setEquals(false);
      return this;
    }
    if (lhs.length != rhs.length) {
      setEquals(false);
      return this;
    }
    for (int i = 0; (i < lhs.length) && (this.isEquals); i++) {
      append(lhs[i], rhs[i]);
    }
    return this;
  }
  /** * * 方法描述 * * @param isEquals * */
  public EqualsBuilder append(char[] lhs, char[] rhs)
  {
    if (!this.isEquals) {
      return this;
    }
    if (lhs == rhs) {
      return this;
    }
    if ((lhs == null) || (rhs == null)) {
      setEquals(false);
      return this;
    }
    if (lhs.length != rhs.length) {
      setEquals(false);
      return this;
    }
    for (int i = 0; (i < lhs.length) && (this.isEquals); i++) {
      append(lhs[i], rhs[i]);
    }
    return this;
  }
  /** * * 方法描述 * * @param isEquals * */
  public EqualsBuilder append(byte[] lhs, byte[] rhs)
  {
    if (!this.isEquals) {
      return this;
    }
    if (lhs == rhs) {
      return this;
    }
    if ((lhs == null) || (rhs == null)) {
      setEquals(false);
      return this;
    }
    if (lhs.length != rhs.length) {
      setEquals(false);
      return this;
    }
    for (int i = 0; (i < lhs.length) && (this.isEquals); i++) {
      append(lhs[i], rhs[i]);
    }
    return this;
  }
  /** * * 方法描述 * * @param isEquals * */
  public EqualsBuilder append(double[] lhs, double[] rhs)
  {
    if (!this.isEquals) {
      return this;
    }
    if (lhs == rhs) {
      return this;
    }
    if ((lhs == null) || (rhs == null)) {
      setEquals(false);
      return this;
    }
    if (lhs.length != rhs.length) {
      setEquals(false);
      return this;
    }
    for (int i = 0; (i < lhs.length) && (this.isEquals); i++) {
      append(lhs[i], rhs[i]);
    }
    return this;
  }
  /** * * 方法描述 * * @param isEquals * */
  public EqualsBuilder append(float[] lhs, float[] rhs)
  {
    if (!this.isEquals) {
      return this;
    }
    if (lhs == rhs) {
      return this;
    }
    if ((lhs == null) || (rhs == null)) {
      setEquals(false);
      return this;
    }
    if (lhs.length != rhs.length) {
      setEquals(false);
      return this;
    }
    for (int i = 0; (i < lhs.length) && (this.isEquals); i++) {
      append(lhs[i], rhs[i]);
    }
    return this;
  }
  /** * * 方法描述 * * @param isEquals * */
  public EqualsBuilder append(boolean[] lhs, boolean[] rhs)
  {
    if (!this.isEquals) {
      return this;
    }
    if (lhs == rhs) {
      return this;
    }
    if ((lhs == null) || (rhs == null)) {
      setEquals(false);
      return this;
    }
    if (lhs.length != rhs.length) {
      setEquals(false);
      return this;
    }
    for (int i = 0; (i < lhs.length) && (this.isEquals); i++) {
      append(lhs[i], rhs[i]);
    }
    return this;
  }
  /** * * 方法描述 * * @param isEquals * */
  public boolean isEquals()
  {
    return this.isEquals;
  }
  /** * * 方法描述 * * @param isEquals * */
  public Boolean build()
  {
    return Boolean.valueOf(isEquals());
  }
  /** * * 方法描述 * * @param isEquals * */
  protected void setEquals(boolean isEquals)
  {
    this.isEquals = isEquals;
  }
  /** * * 方法描述 * * @param isEquals * */
  public void reset()
  {
    this.isEquals = true;
  }
}