Equals in Java USES method aggregation

  • 2020-06-07 04:26:46
  • OfStack

This summary takes the form of asking a question and then giving the answer to the question. This is a current attempt to learn knowledge and make it more purposeful.

Q1. When should an object's equals method be overridden?

Answer: 1 Generally, we need to override the equals method of the object when we need to make a value comparison. The exception is clearly described in article 7 of effective java, "In rewriting equals, please observe the general convention".

We know that in Java, every object inherits from Object. If not overridden, the default equals code looks like this:


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

As you can see from the above code, equal defaults to using "==" to determine whether two objects are equal. Two objects compare the address of the object using "==" and "==" returns true only if both references point to the same object. So, in the first example, you need to override the equals method so that the two objects have equals.

Q2. How to override equals?

A: First, when rewriting the equals method, you need to ensure that its general conventions are met. These conventions are as follows:

Reflexivity. For any reference value x, ES33en.equals (x)1 is set to true.
Symmetry. For any reference value x and y, x.equals (y) also returns true if and only if y.equals (x).
Transitivity, for any reference values x,y,z. If x.equals (y) returns true and y.euqals (z) returns true, then x.equals (z) also returns true.
For any reference values x and y, multiple calls to ES67en.equals (y) either 1 to return true or 1 to return false if the object information used for equals comparisons is not modified.
Non-null, all objects must not be equal to null.

In fact, I think an easy way is to refer to String's equals method, which is officially published to meet all kinds of requirements. The code is shown below


public boolean equals(Object anObject) {
 if (this == anObject) {
 return true;
 }
 if (anObject instanceof String) {
 String anotherString = (String)anObject;
 int n = count;
 if (n == anotherString.count) {
  char v1[] = value;
  char v2[] = anotherString.value;
  int i = offset;
  int j = anotherString.offset;
  while (n �  != 0) {
  if (v1[i++] != v2[j++])
   return false;
  }
  return true;
 }
 }
 return false;
}

The function is explained as follows:

Use == to check if an argument points to a reference to an object. Use instanceof to check that arguments are of the same class as the object. If not, they are not equal. Converts arguments to the correct type. Depending on the class definition, examine each condition that implements the equality of values for this object.

For more information, see article 7 of effective java: "Obey general conventions when rewriting equals."

3. What should I pay attention to when modifying equals?

Answer: Generally, the following points should be noted:

To modify the equals method, modify the hashCode method as well

First of all, this is the language of a contract, the one reason is that when this object is regarded as the elements of the hash containers, need to rely on hashCode, object is the default hashCode return 1 this object hashCode, different object hashCode the return value is not 1 sample, and the hash container processing elements, is according to the object's hash value object allocation to different barrel, if we don't rewrite object hashCode, so values are equal object resulting hash value will also be different, so when to look for in the hash containers, will can't find the corresponding elements.

For more information, see Article 8 of effective Java: "When rewriting equals, always rewrite hashCode".

When overridden, ensure that the function declaration is correct

Please note that equals's statement is


public boolean equals(Object obj)

The parameter type is Object, if the parameter type is this object type, as follows:


class Point{
final int x;
final int y;
public void Point(int x, int y)
 this.x = x;
 this.y = y;
}
public boolean euqals(Point obj){
  return (this.x == obj.x && this.y == obj.y);
 }
}

The following code executes as expected.


Point a(1, 2);
Poinr b(1, 2);
System.out.println(a.equals(b));//  The output true

But if you put the class A in the container, you have a problem


import java.util.HashSet;

HashSet<Point> coll = new HashSet<Point>();
coll.add(a);
System.out.println(coll.contains(b));//  The output false

This is because the contains method in HashSet calls equals(Object obj), while equals(Object obj) in Point is still equals of Object. This method, as mentioned earlier, compares the address of the object, so when you call contains(b) in coll, you certainly don't get true.

Note that equals is correct when there are inheritance relationships
When one class overrides the equals method and another class inherits the class, the symmetry mentioned earlier may be violated, as shown in the code below:


public class ColoredPoint extends Point { 
 private final Color color;
 public ColoredPoint(int x, int y, Color color) {
 super(x, y);
 this.color = color;
 }

 @Override 
 public boolean equals(Object other) {
 boolean result = false;
 if (other instanceof ColoredPoint) {
  ColoredPoint that = (ColoredPoint) other;
  result = (this.color.equals(that.color) && super.equals(that));
 }
 return result;
 }
}

When we compare


Point p = new Point(1, 2);
ColoredPoint cp = new ColoredPoint(1, 2, Color.RED);
System.out.println(p.equals(cp)); // The output ture
System.out.println(cp.equals(p)); // The output false

The reason is that when calling ES157en.equals, only the x and y coordinates of Point are compared, and ColoredPoint is also of Point type, so the code on the third line above is equal, but when calling ColoredPoint, Point is not of ColoredPoint type, resulting in the output of false on the fourth line.

What if we ignore the information of Color to compare? For example, we change the equals method of ColoredPoint to:


@overwrite
public boolean equals(Object obj){
 if((obj instanceof Point)){
 return false;
 }

 if(!(obj instanceof ColoredPoint)){
 return obj.equals(this);
 }

 return super.equals(obj) && ((ColoredPoint)obj).color == color;
}

This guarantees symmetry, but violates transitivity, as follows:


ColoredPoint cp1 = new ColoredPoint(1, 2, Color.RED);
Point p = new Point(1, 2);
ColoredPoint cp2 = new ColoredPoint(1, 2, Color.BLUE);
System.out.println(cp1.equals(p)); //true
System.out.println(p.equals(cp2)); //true
System.out.println(cp1.equals(cp2)); //false

Faced with this situation, there are roughly two solutions, 1 cool shell article how to avoid the hidden trap of equals method in Java the last one, cut off the possibility of Point and ColoredPoint being equal, this is a way of dealing with Point and ColoredPoint are different. The other approach, proposed on effective Java, USES aggregation rather than inheritance and USES Point as a member variable of ColoredPoint. I prefer this approach at the moment because aggregation is more flexible and less coupled than inheritance. The code for this method is as follows:


public boolean equals(Object anObject) {
 if (this == anObject) {
 return true;
 }
 if (anObject instanceof String) {
 String anotherString = (String)anObject;
 int n = count;
 if (n == anotherString.count) {
  char v1[] = value;
  char v2[] = anotherString.value;
  int i = offset;
  int j = anotherString.offset;
  while (n �  != 0) {
  if (v1[i++] != v2[j++])
   return false;
  }
  return true;
 }
 }
 return false;
}
0

When ColoredPoint needs to compare coordinates, it can call the asPoint method to convert to coordinates for comparison. In other cases, compare coordinates and colors to solve the above symmetry and transitivity problems.

The above is the content of the full text, due to the limited level, the article will inevitably have mistakes, I hope you correct, thank you.


Related articles: