Home  >  Article  >  Java  >  How to correctly override the equals method in Java

How to correctly override the equals method in Java

WBOY
WBOYforward
2023-05-09 11:19:261322browse

1. What is the equals method?

We must first know that the Object class is the parent class (super class/base class) of all classes in Java, that is to say, in Java In , all classes inherit from Object class by default. In other words, we can use all the methods implemented in Object class directly. The equals method is one of the many methods implemented by the Object class.

The following screenshots are taken from Java11 API

How to correctly override the equals method in Java

All methods of the Object class:

How to correctly override the equals method in Java

1.1 equals method:

  • equals: It is a method in Object class. It can only determine the reference type. You can bring it later. Everyone, take a look at the jdksource code

  • The default judgment is whether the addresses are equal (because the underlying essence of reference type variables is to store object addresses, those with C/C knowledge Friends should know it very well), this method is often overridden in subclasses to determine whether the contents of objects are equal. For example, Integer and String, which you will learn briefly later (see the source code implementation in IDEA)

2. Why should we rewrite equals method?

We have the Object class and the equals method can be used, right? Why do we have to override the equals method? This depends on the implementation mechanism of the equals method of the Object class.

How to correctly override the equals method in Java

We can clearly see that the bottom layer of the equals method of the Object class is implemented using ==, that is It is said that its usage is consistent with the usage of == which we usually use to compare basic data types. Let’s first take a look at the syntax of ==:

  • == can only be used to compare basic data types for equality, that is, simple value comparison;

  • == There may also be failures when comparing floating point numbers. This is because the storage mechanism of floating point numbers is different from that of the integer family. The floating point number itself cannot represent an accurate value (you can check the specific reasons by yourself). IEEE 754 rules, will not be expanded here)

So we can use == when simply comparing values ​​of basic data types, but we cannot do this when comparing reference data types, as mentioned above It has been mentioned that the reference data type is essentially used to reference/store the address of the object, so you can treat it as a pointer to C/C (here it is said that Java does not have pointers, personally I think it’s just a different name)
Note: Don’t confuse the Java reference with the C reference. The C reference is actually a pointer constant, that is, int* const, this is why the reference of C can only be used as an alias of a variable.

2.1 For example~

When comparing two ints, you can directly use ==. When they are equal, the result is true, and when newWhen two objects with exactly the same attributes are obtained, an error will occur if == is used for comparison. As we can see, we should obviously want to get true, The result is false
Source code:

How to correctly override the equals method in Java

Running result:

How to correctly override the equals method in Java

Go here , we should have a rough idea of ​​why we need to override the equals method when comparing objects, because the Object class provides us with something that is not easy to use~~

3. Analyze the equals source code:

Before rewriting, let’s still take a look at the definition in Java API:
public boolean equals(Object obj)
Function: Indicates whether some other object is "equal" to this object.

The equals method implements equivalence on non-null object references:

  • Reflexivity: for any non-null reference value x, x.equals(x) should return true.

  • Symmetry: For any non-null reference values ​​x and y, x.equals(y) should return trueif and only ify.equals(x)returnstrue.

  • Transitivity: For any non-null reference values ​​x, y and z, if x. equals(y)returnstruey.equals(z)returnstrue, thenx.equals(z) Should return true.

  • Consistency: Multiple calls to x.equals(y) always return for any non-null reference values ​​x and y true Or always return false, provided that the information used in the equals comparison on the object has not been modified.

  • For any non-null reference value x, x.equals(null) should return false.

The equals method of class Object implements the most distinctive possible equivalence relationship on the object; that is, for any non-null reference values ​​x and y, if and only if x and This method returns true when y refers to the same object (x == y has value true).

Note: It is usually necessary to override the hashCode method when overriding this method in order to maintain the normal contract of the hashCode method, which states that equal objects must have equal hash codes.

How to correctly override the equals method in Java

Next look at the equals method overridden in the String class and the overridden Integer class The equals method written:

How to correctly override the equals method in Java

//String类equals源代码:
public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

A simple explanation is that when the comparison is the same object, it directly returns true, Improve efficiency. When the object passed in is an instance of the current class, further judgment is entered. A for loop traverses each character of the string in turn, and returns false as long as one character is different.

Continue to take a look at the equals source code of the Integer class:

How to correctly override the equals method in Java

//Integer类的equals源代码:
 public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

Integer class 's equals source code is much simpler. As long as the incoming object is an instance of the current class, further judgment will be made: when their values ​​​​are equal, true will be returned, otherwise If equal, return false.

Let’s give a practical demonstration ⑧, taking the Integer class as an example:

How to correctly override the equals method in Java

Obviously, we know Integer class overrides the equals method and is a reference type. When == is directly used to compare reference type variables, the result is false, and equals is used to determine the result is true. This well illustrates the necessity of overriding the equals method. String class Please verify it yourself⑧.

4. Correctly rewrite the equals method:

(Let me talk about the conclusion first, getClass() is safer than instanceof)

At this point, we have basically analyzed the various source codes of the equals method. The next step is to implement the equals method ourselves.

Here are two common equals rewriting methods:

  • Use instanceof to implement rewriting equalsmethod

  • Use getClass to implement overriding equalsmethod

Assume there are This scenario:

Override the equals method in the Object class in the already created rectangle class to return # when the length and width of the rectangle are equal. ##TRUE, while overriding the hashCode method, and overriding the toString method to display the length and width information of the rectangle. and test the class.

package com.test10_04;

import java.util.Objects;

class Rectangle {
    private double length;
    private double wide;

    public Rectangle() {
        //空实现
    }
    public Rectangle(double length, double wide) {
        setLength(length);
        setWide(wide);
    }

    public double getLength() {
        return length;
    }

    public void setLength(double length) {
        assert length > 0.0 : "您的输入有误,长方形的长不能小于0";
        this.length = length;
    }

    public double getWide() {
        return wide;
    }

    public void setWide(double wide) {
        assert wide > 0.0 : "您的输入有误,长方形的宽不能小于0";
        this.wide = wide;
    }

    public double area() {
        return this.length * this.wide;

    }
    public double circumference() {
        return 2 * (this.wide + this.length);
    }

    public boolean equals(Object obj) {
        if (this == obj) { //判断一下如果是同一个对象直接返回true,提高效率
            return true;
        }
        if (obj == null || obj.getClass() != this.getClass()) { //如果传进来的对象为null或者二者为不同类,直接返回false
            return false;
        }
        //也可以以下方法:
//        if (obj == null || !(obj instanceof Rectangle)) { //如果传进来的对象为null或者二者为不同类,直接返回false
//            return false;
//        }
        Rectangle rectangle = (Rectangle) obj; //向下转型
        //比较长宽是否相等,注意:浮点数的比较不能简单地用==,会有精度的误差,用Math.abs或者Double.compare
        return Double.compare(rectangle.length, length) == 0 && Double.compare(rectangle.wide, wide) == 0;
    }

    public int hashCode() { //重写equals的同时也要重写hashCode,因为同一对象的hashCode永远相等
        return Objects.hash(length, wide); //调用Objects类,这是Object类的子类
    }

    public String toString() {
        return "Rectangle{" + "length=" + length + ", wide=" + wide + '}';
    }
}

public class TestDemo {
    public static void main(String[] args) {

        Rectangle rectangle1 = new Rectangle(3.0, 2.0);
        Rectangle rectangle2 = new Rectangle(3.0, 2.0);
        
        System.out.println(rectangle1.equals(rectangle2));
        System.out.println("rectangle1哈希码:" + rectangle1.hashCode() +
                "\nrectangle2哈希码:" + rectangle2.hashCode());
        System.out.println("toString打印信息:" + rectangle1.toString());
    }
}

The specific implementation ideas are clearly stated in the code. Let’s focus on analyzing the advantages and disadvantages of the two implementation methods

getClass and instanceof:

How to correctly override the equals method in Java

Simplify the code logic:

Let’s focus on this simple code

//getClass()版本
public class Student {
	private String name;
	
    public void setName(String name) {
        this.name = name;
    }
	@Override
	public boolean equals(Object object){
		if (object == this)
			return true;
		// 使用getClass()判断对象是否属于该类
		if (object == null || object.getClass() != getClass())
			return false;
		Student student = (Student)object;
		return name != null && name.equals(student.name);
}
//instanceof版本
public class Student {
	private String name;
	
    public void setName(String name) {
       this.name = name;
   }
	@Override
	public boolean equals(Object object){
		if (object == this)
			return true;
		// 通过instanceof来判断对象是否属于类
		if (object == null || !(object instanceof Student))
			return false;
		Student student = (Student)object;
		return name!=null && name.equals(student.name);
	}
}

In fact, both solutions are valid, the difference is

getClass() restricts the object to be the same class, but instanceof allows the object to be the same class or its subclass, so the equals method becomes the parent class and subclass The equals operation can also be performed. At this time, if the subclass redefines the equals method, it may become the parent class object equlas. The subclass object is true, but the subclass object equlas the parent class object is false, as shown below:

class GoodStudent extends Student {

    @Override
    public boolean equals(Object object) {
        return false;
    }

    public static void main(String[] args) {
        GoodStudent son = new GoodStudent();
        Student father = new Student();

        son.setName("test");
        father.setName("test");

		// 当使用instance of时
        System.out.println(son.equals(father)); // 这里为false
        System.out.println(father.equals(son)); // 这里为true

		// 当使用getClass()时
        System.out.println(son.equals(father)); // 这里为false
        System.out.println(father.equals(son)); // 这里为false	
    }
}

Note that

getClass() is used here

How to correctly override the equals method in Java

The return values ​​​​are both

false , in line with our expectations, (even if the class is different, it must be false)

How to correctly override the equals method in Java

and replace it with

instanceof Give it a try:

How to correctly override the equals method in Java

Running results: One is true and the other is false, obviously there is a problem.

How to correctly override the equals method in Java

The reasons here are as follows: The syntax of
instanceof is like this:
When an object is an instance of a class, the result Only then is true. But it also has a characteristic that if this object is an instance of its subclass, the result will also be true. This leads to the above bug. That is to say, when the two objects being compared have a parent-child relationship, instanceof may cause problems. **Friends who need to learn more can learn it by themselves, so it is recommended that when rewriting the equals method, try to use getClass to achieve it.

When rewriting the equals method, you need to override the hashCode method.

The above is the detailed content of How to correctly override the equals method in Java. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete