Understanding equals method - java

J. Bloch in his effective Java provides a several rules for the implementation for equals method. Here they are:
• Reflexive: For any non-null reference value x, x.equals(x) must
return true.
• Symmetric: For any non-null reference values x and y,
x.equals(y) must return true if and only if y.equals(x) returns true.
• Transitive: For any non-null reference values x, y, z, if
x.equals(y) returns true and y.equals(z) returns true, then
x.equals(z) must return true.
• Consistent: For any non-null reference
values x and y, multiple invocations of x.equals(y) consistently
return true or consistently return false, provided no information used
in equals comparisons on the objects is modified.
• For any non-null
reference value x, x.equals(null) must return false.
But later in the book he mentioned so-called Liskov substitution principle:
The Liskov substitution principle says that any important property of
a type should also hold for its subtypes, so that any method written
for the type should work equally well on its subtypes
I don't see how it ties to the equals contracts. Should we actually adhere to it while writing the equals implementation?
The question is about implementing the method for subclasses. Here is the example from the book:
private static final Set<Point> unitCircle;
static {
unitCircle = new HashSet<Point>();
unitCircle.add(new Point(1, 0));
unitCircle.add(new Point(0, 1));
unitCircle.add(new Point(-1, 0));
unitCircle.add(new Point(0, -1));
}
public static boolean onUnitCircle(Point p) {
return unitCircle.contains(p);
}
public class CounterPoint extends Point {
private static final AtomicInteger counter = new AtomicInteger();
public CounterPoint(int x, int y) {
super(x, y);
counter.incrementAndGet();
}
public int numberCreated() { return counter.get(); }
}
and the following implementation:
// Broken - violates Liskov substitution principle (page 40)
#Override public boolean equals(Object o) {
if (o == null || o.getClass() != getClass())
return false;
Point p = (Point) o;
return p.x == x && p.y == y;
}
Ok, violates and what then? I don't understand.

There are typically 2 ways how to check the type in the equals method:
Option 1: instanceof
if (! (obj instanceof ThisClass)){
return false;
}
This option respects the Liskov substitution principle. But you cannot add additional properties in sub classes which are relevant for the equals method without breaking the characteristics of an equivalence relation (reflexive, symmetric, transitive).
Option 2: getClass()
if (obj == null || ! this.getClass().equals(obj.getClass())) {
return false;
}
This option violates the Liskov substitution principle. But you can add additional properties in sub classes which are relevant for the equals method without breaking the characteristics of an equivalence relation (reflexive, symmetric, transitive).
Joshua Bloch warns about this in his book "Effective Java".
Angelika Langer however mentions a way for "mixed-tpye" comparisons, if you can define default values for additional properties:
http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals-2.html
The downside is that the equals methods becomes rather complicated.
// Broken - violates Liskov substitution principle (page 40)
#Override public boolean equals(Object o) {
if (o == null || o.getClass() != getClass())
return false;
Point p = (Point) o;
return p.x == x && p.y == y;
}
Ok, violates and what then? I don't understand.
So if you have a sub class such as MyPoint (which might add additional methods but not additional properties/ fields), then
Point p1 = new Point(x, y);
Point p2 = new MyPoint(x, y);
p1.equals(p2) == false
Set<Point> points = new HashSet<>();
points.add(p1);
points.contains(p2) == false;
although both objects really represent the same point.
If you would use option 1 (instanceof) instead, the equals method would return true.

I think he is trying to say that the characteristic of a point is its coordinates. So you would expect this to be true:
new Point(0, 0).equals(new CounterPoint(0, 0));
because the two points have the same coordinates, even if they don't have the same type. But the proposed equals method will return false because the two objects have different classes.
If you think of collections for example, this is true:
new LinkedList().equals(new ArrayList());
The two lists don't have the same type but they have the same content (in this case they are both empty) and are therefore considered equal.

Related

What is the proper way of overriding equals and hashcode where either of two variables can be equal?

Lets say I have the following class
public class DualKey {
int key1;
int key2;
// getters, setters and constructors go here
public void equals(Object obj) {
if (obj == null || ! obj instanceOf DualKey)
return false;
return this.key1 == obj.key1 || this.key1 == obj.key2 || this.key2 == obj.key1 || this.key2 == obj.key2;
}
}
Is it possible to override hashcode in such a way that the equals and hashcode contract is preserved?
PS: I realize that it would probably be better to define a comparator instead, but I'm working with spark, where the only way to define equality is to override the equals method.
No, it is not possible, because that equals() implementation does not meet the Java API requirements:
The equals method implements an equivalence relation on non-null object references:
It is reflexive: for any non-null reference value x, x.equals(x) should return true.
It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
For any non-null reference value x, x.equals(null) should return false.
Specifically, it is not transitive. With your definition, (1,2) == (2,3) and (2,3) == (3,4) but (1,2) != (3,4).
This non-transitivity makes it impossible to implement a non-trivial hash code method. The only thing you can do is return the same number for every object. That'd be a valid implementation, though very poorly performing.

Casting down the hierarchy and equals in java

I have the following code
#Override
public boolean equals(Object o) {
if (!(o instanceof ColorPoint))
return false;
return super.equals(o) && ((ColorPoint) o).color == color;
}
And i have the following
Point p = new Point(1, 2);
ColorPoint cp = new ColorPoint(1, 2, Color.RED);
ColorPoint inherits Point. The problem is when I do p.equals(cp) why it return true? I mean in the last return it call super.equal but at that cast what happens? What it returns at that cast with ColorPoint
#Override public boolean equals(Object o) {
if (!(o instanceof Point))
return false;
Point p = (Point)o;
return p.x == x && p.y == y;
}
This is the equal from Point class
You are using the equals method of Point, not the one of ColorPoint.
Change to cp.equals(p) and you'll get false.
Note that you should not implement equals in a way that could make it asymmetrically. Always check, if the classes match, if you want to extend a class:
// in Point class
#Override
public boolean equals(Object other) {
if (other == null || getClass() != other.getClass()) {
return false;
}
Point p = (Point) other;
return p.x == x && p.y == y;
}
In addition to the other answers your implementation of equals() violates the contract as defined in the JavaDocs:
The equals method implements an equivalence relation on non-null object references:
...
It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
...
That means you should not check for o instanceof Point but for o.getClass() == getClass(). With your implementation you get a different result when calling p.equals(cp) and cp.equals(p) and thus you violate that contract. This could cause subtle bugs since most collections rely on that contract.
Your equals method is implemented for ColorPoint. You call equals on Point. It will only check two coordinates - not a color.
You will get false if you call cp.equals(p);
Class hierarchies and equals() don't go well together.
If you implement equals() in superclass using instanceof (so that subclasses can be equal to superclasses), you break the requirement for equals() to by symmetric:
p.equals(cp) // true
cp.equals(p) // false
If you implement it using getClass().equals(other.getClass()), you'll get correct contract, but prevent any subclass instances from ever being equal to your instance - this may be a problem e.g. when using an ORM like Hibernate that creates proxy classes for your classes.
The only situation where equals() seems to work well across multiple classes is when you have an interface and define the contract of equals() in terms of that interface's methods, then write all the implementations so that they honor the contract and only use the info exposed by the interface. This can be seen for example in java.util.List and its common implementations.

How can I get an equals method with many constrants

I'm working on an equals method that will check to see if two points are equal. A single point consists of quantity and price where quantity is an integer and price is a double. I want the price to stop at the second decimal place which is why I have the TOLERANCE (it is set as 0.01). I am not sure how to get these constraints in the equals(). This is what I have right now:
public boolean equals (Point Other)
{
if((this.quantity == Other.quantity) && (Math.abs(this.price - Other.price)<TOLERANCE)) return true;
return false;
}
Say you have (q1,p1) and (q2,p2). I want to be able to use the equals() to see if:
If q1=q2 while p1=p2 or
If q1!=q2 while p1=p2 or
If q1=q2 while p1!=p2 or
If q1!=q2 while p1!=p2
Also in my TestPoint class when in test to see if the points are equal I want it to print out if:
The points are equal
The quantities are not equal, prices are equal
The quantities are equal, the prices are not equal
The points are not equal
Here is what I have now in my tester:
if (p1.equals(p2)) {
System.out.println("Points are equal");
} else {
System.out.println("Points are not equal");
}
One of the maxims that Joshua Bloch (Effective Java, 2nd Ed.) offers for overriding equals() in Java is the following (paraphrasing):
You shouldn't try to hard to demonstrate equivalence. For example,
for the File class, equals() should not take symbolic links into
account for demonstrating equivalence. Thankfully, it doesn't.
As was pointed out in the comments, your purpose is not to look for equality (rather, to look for nearness). You should not override the equals() method for this purpose and I just wanted to take a moment to point out the hazard of doing so in this case (because the signature of your method differs, I note that you aren't overriding equals, you're overloading it ... but still).
The contract for equals() from the Object class (the parent class for all objects in Java):
It is reflexive: for any non-null reference value x, x.equals(x) should return true.
It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
For any non-null reference value x, x.equals(null) should return false.
While the developer is free to refine this contract, he may not change it. The Collections framework, in particular, relies on this contract for correctness. If you define an equals() method that violates this contract, the Collections classes may fail in ways that can be difficult to debug.
Creating an equivalence test based on tolerances can violate the equals() contract because of loss of transitivity (the 3rd point in the contract).
To illustrate:
Say you wish to say that objects are equal if an integer field is within +/- 10 of another object's same field
myObject a = new myObject(1);
myObject b = new myObject(6);
myObject c = new myObject(11);
For these objects:
a.equals(b) evaluates to true
b.equals(c) evaluates to true
a.equals(c) evaluates to false
Transitivity is violated and thus the equals contract is broken. Defining an equals() method in this way will cause unpredictable behavior for any class that depends on the equals contract for its correctness. The Collections classes, in particular, will not function properly.
public boolean quantityEqualWith (Point other) {
if(this.quantity == other.quantity) return true;
return false;
}
public boolean priceEqualWith (Point other) {
if (Math.abs(this.price - other.price)<TOLERANCE) return true;
return false;
}
public boolean isEqual(Point other) {
if(this.priceEqualWith(other)&&this.quantityEqualWith(other)) return true;
return false;
}
public boolean isQuantityEqual (Point other) {
if((!this.priceEqualWith(other))&&(this.quantityEqualWith(other))) return true;
return false;
}
public boolean isPriceEqual (Point other) {
if((this.priceEqualWith(other))&&(!this.quantityEqualWith(other))) return true;
return false;
}
public boolean isNotEqual(Point other) {
if((!this.priceEqualWith(other))&&(!this.quantityEqualWith(other))) return true;
return false;
}
I don't recommend overriding equals(). However, if you insist, you should override hashcode() as well.
public int comparePriceQuantity(Point Other)
{
if((this.quantity == Other.quantity)){
if(Math.abs(this.price - Other.price)<TOLERANCE){
return 0; // return PQ_EQUAL;
else
return 2; // return Q_EQUAL_P_NOT_EQUAL
}
}
else{
if(Math.abs(this.price - Other.price)<TOLERANCE)
return 1; // return Q_NOT_EQUAL_P_EQUAL
else
return 3; //PQ_NOT_EQUAL
}
}
If q1=q2 while p1=p2 => 0
If q1!=q2 while p1=p2 =>1
If q1=q2 while p1!=p2 =>2
If q1!=q2 while p1!=p2 =>3
Note: Instead of using int, enum should be used for better readability and manageability of code.
enum declaration:
public enum PointTestValues{
QP_EQUAL,
Q_NOT_EQUAL_P_EQUAL,
Q_EQUAL_P_NOT_EQUAL,
PQ_NOT_EQUAL
}
Message Print:
public void messagePrint(PointTestValues testResult){
switch(testResult){
case QP_EQUAL:
// print message
break;
case Q_NOT_EQUAL_P_EQUAL:
// print message
break;
case Q_EQUAL_P_NOT_EQUAL:
// print message
break;
case PQ_NOT_EQUAL:
// print message
break;
}
}
Note: return corresponding enum value instead of int in the method "comparePriceQuantity". I have provided it in comment
Main function:
messagePrint(point1.comparePriceQuantity(point2))

An equals method when the parameter is of type object

I'm learning Java and am having some trouble with casting/polymorphism concepts.
If I have the following method:
public boolean equals(Object x);
Where x could reference an Object of Class Y;
What's the best way to see if x equals y (a variable referencing an obj of Class Y) ? I understand downcasting e.g. (Y) x is bad?
Downcasting is the norm in Object.equals. Before downcasting, check that x is indeed a compliant object with x instanceof Y; to ensure the symmetry of equals, a check x.getClass() == this.getClass() will be necessary at times.
If x is not an instance of your class, you immediately return false.
Downcasting is not bad if you firstly perform the following checks:
Check for null --> x != null
Compare classes at runtime --> getClass().equals(x.getClass())
Then you can downcast x as the class of this, and perform specific comparisons.
Prior to your null check, you may also want to compare references of this and x.
If the references are equal, you don't need to perform any other comparison.
It highly depends on your interpretation of equality. That's why the equals operator can be overridden.
Just do x.equals(y) and do the interpretation of equality by overriding the equals method.
One template method for this is to do:
public boolean equals(Object x) {
if (this == x)
return true;
if (x == null || this.getClass() != x.getClass())
return false;
// Compare attributes as needed here
}
Of course you should check if x != null before, if there are chances it is null.

creating a polymorphic equals expression

my question concerns altering an expression in java to make it polymorphic. The class is called csHash and is used to create hash-tables from a given datatype DT, and a given keytype KT. now, in several methods within the class (delete, search, insert, etc.) the method uses an equals function that, in the non-polymorphic version, simply uses String ( as the key in the non-polymorphic version is of the type String). what i was thinking is to create an equals method written as something along the lines of the following code. my question is... is it a valid means of testing equality?
public boolean equals(Object x) {
String i;
i = new String[1];
String y;
y = new String[1];
i == i + this; // use the + operator to add 'this' object
y == y + (findkey(other))x; // do the same for other object. findkey methods returns KT for a given object, i use this return to typecast the Object x.
int j = i.compareTo(y); // use built in String compare method
if(j = 0) {return true;} // use if else to determine if they are equal
else {return false;}
}
I would start by reading the spec:
Indicates whether some other object is "equal to" this one.
The equals method implements an equivalence relation on non-null
object references:
It is reflexive: for any non-null reference value x, x.equals(x)
should return true. It is symmetric: for any non-null reference values
x and y, x.equals(y) should return true if and only if y.equals(x)
returns true. It is transitive: for any non-null reference values x,
y, and z, if x.equals(y) returns true and y.equals(z) returns true,
then x.equals(z) should return true. It is consistent: for any
non-null reference values x and y, multiple invocations of x.equals(y)
consistently return true or consistently return false, provided no
information used in equals comparisons on the objects is modified. For
any non-null reference value x, x.equals(null) should return false.
The equals method for class Object implements the most discriminating
possible equivalence relation on objects; that is, for any non-null
reference values x and y, this method returns true if and only if x
and y refer to the same object (x == y has the value true).
Note that it is generally necessary to override the hashCode method
whenever this method is overridden, so as to maintain the general
contract for the hashCode method, which states that equal objects must
have equal hash codes.
In other words, as long as given three objects x, y, and z the following will hold:
x.equals(y) implies y.equals(x)
!x.equals(y) implies !y.equals(x)
x == y implies x.equals(y)
x.equals(y) && y.equals(z) implies x.equals(z)
If your equals method follows this contract, then it is a valid Java Object.equals implementation. You can create lots and lots of unit tests for this quite easily to ensure all corner cases are covered.
Note that you will also have to override hashCode with the related behavior.
Your equals method is problematic as it doesn't compare the two hash tables with all items in the buckets. Also it doesn't exclude some cases. An example override of equals method in your CSHash class looks like the following to satisfy the general guideline of equals method:
#Override
public boolean equals(Object o) {
if (o == null)
return false;
if (this == o)
return true;
if (!(o instanceof CSHash))
return false;
CSHash other = (CSHash) o;
return this.entrySet().equals(other.entrySet());
}
Here it assumes that you have defined entrySet() method in your CSHash class to return the set of your hash table. You can define your own entrySet() method similar to the one in Java's HashMap class.

Categories