Javaequals()方法

示例

TL; DR

==测试引用是否相等(它们是否是同一对象

.equals()测试值是否相等(在逻辑上是否为“相等”


equals()是用于比较两个对象是否相等的方法。当且仅当两个引用都指向同一实例时equals(),Object该类中方法的默认实现true才返回。因此,它的行为与通过进行比较的行为相同==。

public class Foo {
    int field1, field2;
    String field3;

    public Foo(int i, int j, String k) {
        field1 = i;
        field2 = j;
        field3 = k;
    }

    public static void main(String[] args) {
        Foo foo1 = new Foo(0, 0, "bar");
        Foo foo2 = new Foo(0, 0, "bar");

        System.out.println(foo1.equals(foo2)); // 打印错误
    }
}

即使foo1和foo2使用相同的字段创建,它们仍指向内存中的两个不同对象。equals()因此,默认实现的计算结果为false。

要比较一个对象的内容是否相等,equals()必须重写。

public class Foo {
    int field1, field2;
    String field3;

    public Foo(int i, int j, String k) {
        field1 = i;
        field2 = j;
        field3 = k;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }

        Foo f = (Foo) obj;
        return field1 == f.field1 &&
               field2 == f.field2 &&
               (field3 == null ? f.field3 == null : field3.equals(f.field3));
    }

    @Override
    public int hashCode() {
        int hash = 1;
        hash = 31 * hash + this.field1;
        hash = 31 * hash + this.field2;
        hash = 31 * hash + (field3 == null ? 0 : field3.hashCode());
        return hash;
    }

    public static void main(String[] args) {
        Foo foo1 = new Foo(0, 0, "bar");
        Foo foo2 = new Foo(0, 0, "bar");

        System.out.println(foo1.equals(foo2)); // 打印真实
    }
}

equals()如果对象的字段相同,则覆盖方法在此处确定对象相等。

注意,该hashCode()方法也被覆盖。该方法的约定规定,当两个对象相等时,它们的哈希值也必须相同。这就是为什么人们几乎总是必须超越hashCode()并equals()在一起。

特别注意方法的参数类型equals。是的Object obj,不是Foo obj。如果将后者放入您的方法中,则这不是该equals方法的替代。

在编写自己的类时,您必须在覆盖equals()和时编写类似的逻辑hashCode()。大多数IDE可以自动为您生成此代码。

equals()可以在String类中找到实现的示例,该类是核心Java API的一部分。String该类不是比较指针,而是比较的内容String。

Java SE 7

Java 1.7引入了java.util.Objects提供便利方法的类,该类equals比较了两个潜在的null引用,因此可以用来简化该equals方法的实现。

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null || getClass() != obj.getClass()) {
        return false;
    }

    Foo f = (Foo) obj;
    return field1 == f.field1 && field2 == f.field2 && Objects.equals(field3, f.field3);
}

类比较

由于equals方法可以针对任何对象运行,因此该方法经常要做的第一件事(检查null)是检查被比较对象的类是否与当前类匹配。

@Override
public boolean equals(Object obj) {
    //...检查是否为空
    if (getClass() != obj.getClass()) {
        return false;
    }
    //...比较字段
}

通常,通过比较类对象,如上所述完成此操作。但是,这在一些不太明显的特殊情况下可能会失败。例如,某些框架生成类的动态代理,而这些动态代理实际上是不同的类。这是使用JPA的示例。

Foo detachedInstance = ...
Foo mergedInstance = entityManager.merge(detachedInstance);
if (mergedInstance.equals(detachedInstance)) {
    //如果使用getClass()测试相等性,将永远无法到达这里
    //因为mergedInstance是Foo的代理(子类)
}

解决该限制的一种机制是使用 instanceof

@Override
public final boolean equals(Object obj) {
    if (!(obj instanceof Foo)) {
        return false;
    }
    //...比较字段
}

但是,使用时必须避免一些陷阱instanceof。由于Foo可能具有其他子类,并且这些子类可能会覆盖,因此equals()您可能会遇到aFoo等于aFooSubclass但aFooSubclass不等于的情况Foo。

Foo foo = new Foo(7);
FooSubclass fooSubclass = new FooSubclass(7, false);
foo.equals(fooSubclass) //true
fooSubclass.equals(foo) //false

这违反了对称性和传递性的属性,因此是该equals()方法的无效实现。结果,当使用时instanceof,一个好的做法是制作该equals()方法final(如上例所示)。这将确保没有子类覆盖equals()并违反关键假设。