Determining Equality
It's up to each class's designer to determine what it means for an instance to be "equal to" another object, by coding the comparisons used in the class's equals method.
Often this consists of comparing the values of all the instance fields of the two objects.
-
==equality for primitive integer-type and boolean instance fields. -
floatanddoubleusually should not be compared for exact equality with==, since two "equal" values as far as your class is concerned might have slightly different fractional values due to roundoff error.- One trick is to compare two floats to see if they're "close enough":
double d1 = 3.1415926535897932384626433; double d2 = 3.14159; System.out.println(d1 == d2); // false System.out.println( Math.abs(d1 - d2) < .00001 ); // true -
.equals()equality for reference instance fields. -
If both objects have a
nullfor a given reference field they might still be considered equal. -
staticfields are not considered part of an object's state.
The equals method is important throughout Java, and when writing yours you are expected to follow a contract:
Practice Exercise¶
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 returntrue.- It is symmetric: for any non-null reference values
xandy,x.equals(y)should returntrueif and only ify.equals(x)returnstrue.- It is transitive: for any non-null reference values
x,y, andz, ifx.equals(y)returnstrueandy.equals(z)returnstrue, thenx.equals(z)should returntrue.- It is consistent: for any non-null reference values
xandy, multiple invocations ofx.equals(y)consistently returntrueor consistently returnfalse, provided no information used inequalscomparisons on the objects is modified.- For any non-null reference value
x,x.equals(null)should returnfalse.1
equals and Inheritance¶
A subclass doesn't have access to private fields of a superclass, so a subclass equals should call super.equals() to perform comparison of private superclass fields.
public class Product {
private int id;
String name;
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (this.getClass() != obj.getClass()) return false;
Product other = (Product) obj;
if (this.id != other.id) return false;
if (this.name == null) {
if (other.name != null)
return false;
}
else if (!this.name.equals(other.name))
return false;
return true;
}
// ...
}
public class RatedProduct extends Product {
private int rating;
public boolean equals(Object obj) {
if (this == obj) return true;
if (!super.equals(obj)) return false; // Compare private superclass fields
if (this.getClass() != obj.getClass()) return false;
RatedProduct other = (RatedProduct) obj;
if (this.rating != other.rating) return false;
return true;
}
//...
}
Choose Fields to Compare¶
You don't necessarily have to include all instance fields in your equals method.
-
For example, you application may have a
Userclass where each user is identified by their unique email address, but also has a name, screen name, creation date, and other properties. -
In this case, you might decide it makes sense to only compare the email field in
equals, or maybe email and creation date.
Practice Exercise¶
In Java, serialization is when an object is converted to a stream of data that can be written out and saved, to be read in again later and converted back into an object. This isn't very common, and there are specific declarations and requirements for setting up a class for serialization.
In a serializable object, an instance field that isn't considered part of an object's state (such as an instance field only used in internal calculations, or that is derived from other instance fields) can be declared with the keyword
transientto indicate its value should not be included in the serialization output. The author of the class would also leave these fields out of itsequals.
[1] https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals