作为Java程序员,你真的了解equals方法吗

作为Java程序员,你真的了解equals方法吗

Java编程语言中,一切皆对象。而所有类的超类都是Object对象,本文我们来了解一下Object类中的equals方法。

Object类

首先我们来看一下Object类中equals方法的定义:

public class Object {
    public Object() {};
    public boolean equals(Object obj) {
        return (this == obj);
    }
}

equals方法用来表明其他对象是否和当前对象相等。
如果当前对象是非空对象,则equals方法需要满足如下性质:
(1)自反性:对于任何非空对象x,x.equals(x)应该返回true,即一个对象应该和它本省相等。
(2)对称性:对于任何非空对象x和y,当且仅当y.equals(x)返回true时,x.equals(y)返回true
(3)可传递性:对于任何非空对象x、y和z,如果x.equals(y)返回truey.equals(z)返回true,那么x.equals(z)应该返回true
(4)一致性:对于任何非空的对象x和y,x.equals(y)的多次调用始终返回truefalse,前提是没有修改对象上equals比较中使用的信息。
(5)对于任何非空对象x,x.equals(null)应返回false,即任何非空对象都和空对象不相等。

对于Object类实现的equals方法来说,它肯定是满足上面五个条件的。那么,对于我们自定义的类来说,要不是对equals方法进行重写?如果需要进行重写,应该如何进行重写来满足上面五个条件呢?

到底要不要重写equals方法?

不需要重写equals方法的几种场景:
(1)类的每个实例本质上都是唯一的,比如Thread,或枚举类;
(2)类没有必要提供“逻辑相等”的功能;
(3)超类已经实现了equals方法,而且超类的行为对于子类也是合适的,比如Set接口的大多数实现类直接继承AbstractSetequals实现;
(4)对于privateprotected的类,可以确保它的equals方法永远不会被调用。

需要重写equals方法的集中场景:
如果类具有自己特有的“逻辑相等”概念,而且超类还没有重写equals方法。我们在调用equals方法的时候,希望知道两个对象在“逻辑上”是否相等,而不是它们是否指向了同一个对象,这个时候就必须重写equals方法,比如IntegerString类。

高质量重写equals方法的诀窍

(1)使用==操作符首先检查“参数是否为这个对象的引用”,如果是,则返回true,这是一种性能优化。
(2)使用instanceof操作符检查“参数是否为正确的类型”,如果不是,则返回false。一般而言,正确的类型指的是“equals方法所在的类”。某些情况下,是指该类实现的某个接口。如果类实现的接口改进了equals约定,允许在实现了该接口的类之间进行比较,那么就使用接口,比如SetMapList
(3)将参数转换成正确的类型。
(4)对于该类中的每个关键属性,检查参数中的值是否与该对象中的值匹配。如果这些测试全都成功,则返回true;否则返回false

代码实战

定义一个Point类,有两个属性xy分别表示横坐标和纵坐标。两个点相等的条件是:横坐标相等&纵坐标相等。

public class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Point)) {
            return false;
        }
        Point point = (Point) o;
        return x == point.x && y == point.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }
}

ColorPoint类增加了Color属性,但是不重写父类Pointequals方法,认为点的坐标只要相等,两个点(无论是Point还是ColorPoint)就是相等的。

public class ColorPoint extends Point{
    private final Color color;

    public ColorPoint(int x, int y, Color color) {
        super(x, y);
        this.color = color;
    }
}

测试代码

class PointTest {

    @Test
    void should_return_true_when_compare_x_and_x() {
        Point x = new Point(3, 5);
        assertTrue(x.equals(x));
    }

    @Test
    void should_return_false_when_compare_x_and_null() {
        Point x = new Point(3, 5);
        assertFalse(x.equals(null));
    }

    @Test
    void should_return_false_compare_a_point_and_non_point_object() {
        Point x = new Point(3, 5);
        assertFalse(x.equals("(3, 4)"));
    }

    @Test
    void should_return_true_when_compare_a_point_and_another_point_given_same_x_and_y() {
        Point x = new Point(3, 5);
        Point y = new Point(3, 5);

        assertTrue(x.equals(y));
        assertTrue(y.equals(x));
    }

    @Test
    void should_return_true_when_compare_a_point_and_another_point_given_same_x_and_y() {
        Point x = new Point(3, 5);
        ColorPoint y = new ColorPoint(3, 5, new Color(1111));

        assertTrue(x.equals(y));
        assertTrue(y.equals(x));
    }

}

不同数据类型的比较方法

(1)对于对象,可以递归地调用equals方法
(2)对于非floatdouble的基本类型,使用==操作符比较即可;对于floatdouble类型,分别使用静态方法Float.compare(float, float)Double.compare(double, double)进行比较。
(3)对于数组,则要将上面原则应用到每一个元素上。如果要比较数组所有元素,可以使用Arrays.equals方法。

equals方法相关的其他一些tips

(1)重写equals方法时一定要同时重写hashCode方法;
(2)在重写equals方法的时候不要改变参数的类型

public class Point {
    ...
    public boolean equals(Point p) {
        ...
}

上面这种写法是对equals方法进行了重载(overload),而不是重写(override)。

总结

(1)除非确实有必要,否则不要重写equals方法。
(2)重写equals方法时,要注意是否满足了自反性、对称性、传递性、一致性。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值