I recently talked with my colleagues about the difference between equals and ==. This is actually a very old and simple question, but when you want to override the equals method yourself, you find that there are some things you don't know but have to know. Covering equals requires a lot of attention. Although Object is a very specific class, its main role is to extend. All his non-final methods have clear general conventions. Because they are designed to be overridden methods. Any class that overrides equals, hashCode, toString, clone, and finalize is responsible for complying with the general conventions of these methods. If this cannot be done, it will be difficult to achieve the desired effect when multiple classes are combined.
Overriding the equals method seems simple, but there are many overriding methods that can cause errors. The easiest way to avoid this error is to not override equals, in which case each class instance will only be equal to itself. So under what circumstances can we choose not to override the equals method?
Each instance of a class is inherently unique
This is true for classes that represent active entities rather than values, such as each thread instance. It is meaningless for us to compare it with the equals method, because each thread is unique. In this case, we don't need to override the equals method, because the equals in the Object class is completely sufficient.
Implementation of equals method in Object class:
public boolean equals(Object obj) { return (this == obj); }
Doesn’t care whether the class requires logical equality judgment
Some classes are "numeric classes" that compare sizes and do mathematical operations. In this case, we need to compare the values stored in the class and make a logical equality judgment. In addition to the classes, most classes do not have the concept of "whether one is equal to the other". Such classes that do not care about logical equality do not need to override the equals method.
The equals implemented by the super class is applicable to the subclass
For example, there is no difference in the equals method of the HashSet class that inherits the AbstractSet class, then HashSet directly Just use the equals method of AbstractSet.
Contrary to the above, the situation when we need to override the equals method is: if the class has its own concept of logical equality, and the parent class There is no equals method override available. At this time we need to cover it ourselves.
The equals method implements the equivalence relationship. I have learned the concept of equivalence relations in discrete mathematics. For a binary relation on R, if it satisfies spontaneous symmetry and transitivity, then it is equivalent. Let's analyze the relationship between equals and these three properties in detail.
Reflexivity: For any non-null reference value x, x.equals(x) must return true
Symmetry: For For any non-null reference values x and y, x.equals(y) must return true if and only if y.equals(x) returns true
Transitivity: For any For non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) also returns true, then x.equals(z) must return true.
Reflexivity: ∀ a ∈A, => (a, a) ∈ R
Symmetry: (a, b) ∈R∧ a ≠ b => (b, a) ∈R
Transitivity: (a, b)∈R, (b, c)∈R =>(a, c)∈R
Compare these three properties, there is no problem. It can be seen that the equals method realizes the equivalence relationship.
The equals method of Object simply looks at the address, which is obviously impossible to meet our requirements. So how can we ensure that we write a high-quality, logical comparison method when we write the equals method ourselves? The writing of equals can be summarized in the following four steps:
1. Use the == operator to check whether the parameter is just a reference to the object
If the results are equal, true will be returned, indicating that x and y They are different references to an object, and no further judgment is needed.
2. Use the instanceof operator to check whether the parameter type is correct
If the result is not of the correct type, false is returned, because our equals method inherits from the Object class, so the parameters The unavoidable type is Object. We first use instanceof to judge the type of the parameters. If the types are not correct, there is no need to make the next step of judgment.
3. Convert the parameters into the correct type
Because the detection was done before, there is no problem with the type conversion in this step.
4. Judge the domain values that need to be logically compared in each class
Having ensured that x and y are different instances of the same type, set the fields that need to be judged by logical comparison. The value can be taken out for comparison and judgment. Returns true if all are correct, false otherwise.
After writing the equals method, you must repeatedly judge whether it conforms to reflexivity, symmetry and transitivity. Not only that, but there are some things worth noting when writing the equals method while ensuring equality, and we need to improve on this.
Always override the hashCode method when overriding the equals method
If we write a class related to hashing, we must override the hashCode method when overriding the equals method. Because in a hash table, logically identical objects should have the same hash code. To give a relatively simple example: store a String in a HashSet. It is possible that two String strings with the same content are judged to be false using ==, but only one copy of them exists in the HashSet. This is because Strings with the same logic have the same hashCode.
Generally, if you override the equals method for your class, it proves that under certain circumstances there will be two different objects that are logically equal. If it is related to hashing at this time, then the two objects need the same hashCode. Therefore, when overriding the equals method, always override the hashCode method.
Don’t make the equals method too smart
If we simply follow the above implementation process to write the equals method, it will comply with the regulations and will not cause strange errors. But if we insist on pursuing various and fancy equivalence relationships and make the code bloated, it will not only violate the original intention of high cohesion, but also cause some inexplicable errors in the code.
Don’t get the parameter type of the equals method wrong
It may feel funny to say it, but this is what happens. After modifying the parameter type, the equals method has nothing to do with the Object class. The compiler will not report an error, leaving only endless headaches for programmers. If you don't realize that the parameter type is Object, you can probably spend hours wondering why your program isn't working properly.
I recently talked with my colleagues about the difference between equals and ==. This is actually a very old and simple question, but when you want to override the equals method yourself, you find that there are some things you don't know but have to know. Covering equals requires a lot of attention. Although Object is a very specific class, its main role is to extend. All his non-final methods have clear general conventions. Because they are designed to be overridden methods. Any class that overrides equals, hashCode, toString, clone, and finalize is responsible for complying with the general conventions of these methods. If this cannot be done, it will be difficult to achieve the desired effect when multiple classes are combined.
Overriding the equals method seems simple, but there are many overriding methods that can cause errors. The easiest way to avoid this error is to not override equals, in which case each class instance will only be equal to itself. So under what circumstances can we choose not to override the equals method?
Each instance of a class is inherently unique
This is true for classes that represent active entities rather than values, such as each thread instance. It is meaningless for us to compare it with the equals method, because each thread is unique. In this case, we do not need to override the equals method, because the equals in the Object class is completely sufficient.
Implementation of equals method in Object class:
public boolean equals(Object obj) { return (this == obj); }
Doesn’t care whether the class requires logical equality judgment
Some classes are "numeric classes" that compare sizes and do mathematical operations. In this case, we need to compare the values stored in the class and make a logical equality judgment. In addition to the classes, most classes do not have the concept of "whether one is equal to the other". Such classes that do not care about logical equality do not need to override the equals method.
The equals implemented by the super class is applicable to the subclass
For example, there is no difference in the equals method of the HashSet class that inherits the AbstractSet class, then HashSet directly Just use the equals method of AbstractSet.
Contrary to the above, the situation when we need to override the equals method is: if the class has its own concept of logical equality, and the parent class There is no equals method override available. At this time we need to cover it ourselves.
The equals method implements the equivalence relationship. I have learned the concept of equivalence relations in discrete mathematics. For a binary relation on R, if it satisfies spontaneous symmetry and transitivity, then it is equivalent. Let's analyze the relationship between equals and these three properties in detail.
Reflexivity: For any non-null reference value x, x.equals(x) must return true
Symmetry: For For any non-null reference values x and y, if and only if y.equals(x) returns true, x.equals(y) must return true
Transitivity: For any For non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) also returns true, then x.equals(z) must return true.
Reflexivity: ∀ a ∈A, => (a, a) ∈ R
Symmetry: (a, b) ∈R∧ a ≠ b => (b, a) ∈R
Transitivity: (a, b)∈R, (b, c)∈R =>(a, c)∈R
Compare these three properties, there is no problem. It can be seen that the equals method realizes the equivalence relationship.
Object's equals method simply looks at the address, which is obviously impossible to meet our requirements. So how can we ensure that we write a high-quality, logical comparison method when we write the equals method ourselves? The writing of equals can be summarized in the following four steps:
1. Use the == operator to check whether the parameter is just a reference to the object
If the results are equal, true will be returned, indicating that x and y They are different references to an object, and no further judgment is needed.
2. Use the instanceof operator to check whether the parameter type is correct
If the result is not of the correct type, false is returned, because our equals method inherits from the Object class, so the parameters The unavoidable type is Object. We first use instanceof to judge the type of the parameters. If the types are not correct, there is no need to make the next step of judgment.
3. Convert the parameters into the correct type
Because the detection was done before, there is no problem with the type conversion in this step.
4. Judge the domain values that need to be logically compared in each class
Having ensured that x and y are different instances of the same type, set the fields that need to be judged by logical comparison. The value can be taken out for comparison and judgment. Returns true if all are correct, false otherwise.
After writing the equals method, you must repeatedly judge whether it conforms to reflexivity, symmetry and transitivity. Not only that, but there are some things worth noting when writing the equals method while ensuring equality, and we need to improve on this.
Always override the hashCode method when overriding the equals method
If we write a class related to hashing, we must override the hashCode method when overriding the equals method. Because in a hash table, logically identical objects should have the same hash code. To give a relatively simple example: store a String in a HashSet. It is possible that two String strings with the same content are judged to be false using ==, but only one copy of them exists in the HashSet. This is because Strings with the same logic have the same hashCode.
Generally, if you override the equals method for your class, it proves that under certain circumstances there will be two different objects that are logically equal. If it is related to hashing at this time, then the two objects need the same hashCode. Therefore, when overriding the equals method, always override the hashCode method.
Don’t make the equals method too smart
If we simply follow the above implementation process to write the equals method, it will comply with the regulations and will not cause strange errors. But if we insist on pursuing various and fancy equivalence relationships and make the code bloated, it will not only violate the original intention of high cohesion, but also cause some inexplicable errors in the code.
Don’t get the parameter type of the equals method wrong
It may feel funny to say it, but this is what happens. After modifying the parameter type, the equals method has nothing to do with the Object class. The compiler will not report an error, leaving only endless headaches for programmers. If you don't realize that the parameter type is Object, you can probably spend hours wondering why your program isn't working properly.
The above is the detailed content of An overview of the deeper methods of overriding equals in Java. For more information, please follow other related articles on the PHP Chinese website!