Good question! Yes, you're right. The knowledge that often looks very simple actually are very difficult.
It's too difficult to give the perfect implementation for method "hashCode" and "equals" of non-final class! (For any language, not only java)
Let's discuss two issues: (1) and (2)
(1) For stable objects, there are 2 classic ways to implement equals
(A) Too lax way
Code:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MyClass)) return false;
return this.x == this.x && this.y == this.y;
}
This way is too lax, suppose there are 2 classes: A and B, A is super class and B is derived class. It will cause "a.equals(b)" is true but "b.equals(a)" is false.
But it can handle the object proxy very well, so it's often used in the equals method of ORM entity class.
(B) Too strict way
Code:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || this.getClass() != o.getClass()) return false;
return this.x == this.x && this.y == this.y;
}
This way is very strict, that's why it's the default generated code of IDE such as eclipse. But it can't used to handle the object proxy(Proxy is a popular technology, especially for ORM). For example:
Code:
public class MyProxy extends MyClass {
private MyClass target;
public MyProxy(MyClass target) { this.target = target; }
@Override
public int getX() { return this.target.getX(); }
@Override
public void setX(int x) { this.target.setX(x); }
...other methods such as getY and setY, hashCode...
@Override
public boolean equals(Object o) { return this.target.equals(o); }
}
This way will cause "new MyClass(3, 4).equals(new MyProxy(new MyClass(3, 4))" returns the wrong result "false", that's why I said this way is too strict.
(2) For data object, especially for ORM entity objects, hashCode is very hard to be implemented.
Code:
@Entity
public class MyEntity {
@Column
private String uniqueName;
public String getUniqueName() { return this.uniqueName; }
public void setUniqueName(String uniqueName) { this.uniqueName = uniqueName; }
@Override
public int hashCode() {
return java.util.Objects.hashCode(this.uniqueName);
}
...other methods...
}
This class uses the field "uniqueName" to calculate the hashCode, but don't forget the "uniqueName" can be changed by the method "setUniqueName" after the current object has been added into some HashSets or HashMaps, if it's changed, the set and maps retain this object will be broken, but it's very hard to guarantee no body will change an object which is retained by sets and maps.
This is called unstable class, ORM entity classes are often unstable classes, that's why the hashCode and equals method for ORM entity is harder to be implemented.
I've thought these problems for several years. all of them have been resolved in my framework
https://github.com/babyfish-ct/babyfish
.
For issue(1)
This framework supports new way to implement method "equals", it's neither too lax nor too strict, between (1.A) and (1.B) of this topic. Please see ${babyfish_dir}/demo/babyfishdemo-fundation/.../src/test/java/org/babyfishdemo/foundation/equality/clazz/PointEqualityTest.java to understand my solution.
For issue(2)
This framework supports new collection framework that does not require the method "hashCode" and "equals" of the object itself, but requires a 3rd-party rule, this new collection framework also supports "Unstable Collection Elements", If an unstable object has been added into some sets or added to some maps as key, it can still be changed by user, all the sets and maps depend on it will be adjusted automatiaclly when it's changed. Please see ${babyfish_dir}/demo/babyfishdemo-xcollection/src/test/java/org/babyfishdemo/xcollection/uce/UnstableCollectionElementsTest.java to understand my solution.
By the way, under the root directory, a file "fast-learn_zh_CN.docx" is very useful to know the whole framework very quickly but it's wrote by Chinese, English version will be added in one week, please wait :)