原文链接: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;
}
}