77 lines
2.3 KiB
Plaintext
77 lines
2.3 KiB
Plaintext
A key facet of the ``++equals++`` contract is that if ``++a.equals(b)++`` then ``++b.equals(a)++``, i.e. that the relationship is symmetric.
|
|
|
|
|
|
Using ``++instanceof++`` breaks the contract when there are subclasses, because while the child is an ``++instanceof++`` the parent, the parent is not an ``++instanceof++`` the child. For instance, assume that ``++Raspberry extends Fruit++`` and adds some fields (requiring a new implementation of ``++equals++``):
|
|
|
|
----
|
|
Fruit fruit = new Fruit();
|
|
Raspberry raspberry = new Raspberry();
|
|
|
|
if (raspberry instanceof Fruit) { ... } // true
|
|
if (fruit instanceof Raspberry) { ... } // false
|
|
----
|
|
|
|
If similar ``++instanceof++`` checks were used in the classes' ``++equals++`` methods, the symmetry principle would be broken:
|
|
|
|
----
|
|
raspberry.equals(fruit); // false
|
|
fruit.equals(raspberry); //true
|
|
----
|
|
Additionally, non ``++final++`` classes shouldn't use a hardcoded class name in the ``++equals++`` method because doing so breaks the method for subclasses. Instead, make the comparison dynamic.
|
|
|
|
|
|
Further, comparing to an unrelated class type breaks the contract for that unrelated type, because while ``++thisClass.equals(unrelatedClass)++`` can return true, ``++unrelatedClass.equals(thisClass)++`` will not.
|
|
|
|
|
|
== Noncompliant Code Example
|
|
|
|
----
|
|
public class Fruit extends Food {
|
|
private Season ripe;
|
|
|
|
public boolean equals(Object obj) {
|
|
if (obj == this) {
|
|
return true;
|
|
}
|
|
if (obj == null) {
|
|
return false;
|
|
}
|
|
if (Fruit.class == obj.getClass()) { // Noncompliant; broken for child classes
|
|
return ripe.equals(((Fruit)obj).getRipe());
|
|
}
|
|
if (obj instanceof Fruit ) { // Noncompliant; broken for child classes
|
|
return ripe.equals(((Fruit)obj).getRipe());
|
|
}
|
|
else if (obj instanceof Season) { // Noncompliant; symmetry broken for Season class
|
|
// ...
|
|
}
|
|
//...
|
|
----
|
|
|
|
|
|
== Compliant Solution
|
|
|
|
----
|
|
public class Fruit extends Food {
|
|
private Season ripe;
|
|
|
|
public boolean equals(Object obj) {
|
|
if (obj == this) {
|
|
return true;
|
|
}
|
|
if (obj == null) {
|
|
return false;
|
|
}
|
|
if (this.getClass() == obj.getClass()) {
|
|
return ripe.equals(((Fruit)obj).getRipe());
|
|
}
|
|
return false;
|
|
}
|
|
----
|
|
|
|
|
|
== See
|
|
|
|
* https://wiki.sei.cmu.edu/confluence/x/AzZGBQ[CERT, MET08-J.] - Preserve the equality contract when overriding the equals() method
|
|
|