Right Way to Implement Equals Contract

Right way to implement equals contract

This is (almost) correct for "technical equality", but not for "natural equality". To achieve top technical equality, you should also test the reflexive o == this. It may happen that the object isn't persisted in DB yet and thus doesn't have a technical ID yet. E.g.

public class User {

private Long id;

@Override
public boolean equals(Object object) {
return (object instanceof User) && (id != null)
? id.equals(((User) object).id)
: (object == this);
}

@Override
public int hashCode() {
return (id != null)
? (User.class.hashCode() + id.hashCode())
: super.hashCode();
}

}

For "natural equality" you should rather compare all non-technical properties. For "real world entities" this is after all more robust (but also more expensive) than technical equality.

public class User {

private String name;
private Date birth;
private int housenumber;
private long phonenumber;

@Override
public boolean equals(Object object) {
// Basic checks.
if (object == this) return true;
if (!(object instanceof User)) return false;

// Property checks.
User other = (User) object;
return Objects.equals(name, other.name)
&& Objects.equals(birth, other.birth)
&& (housenumber == other.housenumber)
&& (phonenumber == other.phonenumber);
}

@Override
public int hashCode() {
return Objects.hash(name, birth, housenumber, phonenumber);
}

}

True, that's lot of code when there are a lot of properties. A bit decent IDE (Eclipse, Netbeans, etc) can just autogenerate equals(), hashCode() (and also toString(), getters and setters) for you. Take benefit of it. In Eclipse, rightclick code and peek the Source (Alt+Shift+S) menu option.

See also:

  • JBoss: Equals and HashCode (in view of persistence)
  • Hibernate: Persistent Classes - implementing equals() and hashCode()
  • Related SO question: Overriding equals and hashCode in Java

How to obey contract of equals() when deriving from abstract class

Java's equals contract gets especially spotty in situations like this, and in the end it all becomes a matter of the programmer's preferences and needs. I remember reaching this very same issue and I came across this article, which goes over several possibilities and issues when considering the equals contract with Java. It basically ends up saying there's no way to properly do it without breaking the Java equals contract.

When dealing with abstract classes, my personal preference is to not give the abstract class an equals method at all. It doesn't make sense. You can't have two objects of an abstract type; how should you compare them? Instead, I give each subclass its own equals, and the runtime handles the rest whenever equals() is called. And overall, the solution presented in the article that I most often follow is the "only objects of the exact same class may be compared", which seems the most sensible to me.

What is the proper way to implement a robust equals() and hashCode() method in an inheritance hierarchy?

Here are my notes from reading Effective Java 2nd Edition:

Equals
must adhere to general contract:

  • Reflexive: for non-null x: x.equals(x) == true
  • Symmetric: for non-null x,y: x.equals(y) <==> y.equals(x)
  • Transitive: for non-null x,y,z: x.equals(y) and y.equals(z) ==> x.equals(z) == true
  • Consistent: for any non-null x,y: if x.equals(y) == true, then for all invocations must return true if no change in x and y
  • Null: for non-null x: x.equals(null) == false

High Quality equals method:

  1. Using == to check if the argument is a reference to this object (x == x)
  2. Use instanceof to check if argument is the correct type (also checks for null)
  3. Cast the argument to correct type
  4. For each "significant" field in the class, check if that field of the argument matches the corresponding field of this object
  5. After done, check if Symmetric, transitive and consistent

Final caveats:

  • always override hashCode when you override equals
  • Don't try to be too clever
  • don't substitute another type for Object in the equals declaration -> Not worth it minor performance gains for added complexity

Hashcode direct quote from Effective Java 2nd Edition

  1. Store some constant nonzero value, say, 17, in an int variable called result.
  2. For each significant field f in your object (each field taken into account by the
    equals method, that is), do the following:

    • Compute an int hash code c for the field:

      1. If the field is a boolean, compute (f ? 1 : 0).
      2. If the field is a byte, char, short, or int, compute (int) f.
      3. If the field is a long, compute (int) (f ^ (f >>> 32)).
      4. If the field is a float, compute Float.floatToIntBits(f).
      5. If the field is a double, compute Double.doubleToLongBits(f), and
        then hash the resulting long.
      6. If the field is an object reference and this class’s equals method
        compares the field by recursively invoking equals, recursively
        invoke hashCode on the field. If a more complex comparison is
        required, compute a “canonical representation” for this field and
        invoke hashCode on the canonical representation. If the value of the
        field is null, return 0 (or some other constant, but 0 is traditional).
      7. If the field is an array, treat it as if each element were a separate field.
        That is, compute a hash code for each significant element by applying
        these rules recursively, and combine these values per step 2.b. If every
        element in an array field is significant, you can use one of the
        Arrays.hashCode methods added in release 1.5.
    • Combine the hash code c computed in step 2.a into result as follows:
      result = 31 * result + c;
  3. Return result.

  4. When you are finished writing the hashCode method, ask yourself whether
    equal instances have equal hash codes. Write unit tests to verify your intuition!

so following these rules:

@Override
public boolean equals(Object obj) {

if (this == obj) {
return true;
}

if (!(obj instanceof Employee)) {
return false;
}
Employee other = (Employee) obj;

return super.equals(other) &&
Double.compare(this.salary, other.salary) == 0 &&
this.hireDay.equals(other.hireDay);

}

In your case though it seems like id should already uniquely identify any person, so you should just use that for comparison in equals and not override it in any subclass.

How to override equals method in Java

//Written by K@stackoverflow
public class Main {

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ArrayList<Person> people = new ArrayList<Person>();
people.add(new Person("Subash Adhikari", 28));
people.add(new Person("K", 28));
people.add(new Person("StackOverflow", 4));
people.add(new Person("Subash Adhikari", 28));

for (int i = 0; i < people.size() - 1; i++) {
for (int y = i + 1; y <= people.size() - 1; y++) {
boolean check = people.get(i).equals(people.get(y));

System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
System.out.println(check);
}
}
}
}

//written by K@stackoverflow
public class Person {
private String name;
private int age;

public Person(String name, int age){
this.name = name;
this.age = age;
}

@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}

if (obj.getClass() != this.getClass()) {
return false;
}

final Person other = (Person) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}

if (this.age != other.age) {
return false;
}

return true;
}

@Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 53 * hash + this.age;
return hash;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

Output:

run:

-- Subash Adhikari - VS - K false

-- Subash Adhikari - VS - StackOverflow false

-- Subash Adhikari - VS - Subash Adhikari true

-- K - VS - StackOverflow false

-- K - VS - Subash Adhikari false

-- StackOverflow - VS - Subash Adhikari false

-- BUILD SUCCESSFUL (total time: 0 seconds)

How to implement equals with hibernate without risking losing the symmetric property?

After rading up some more i summarize this question with:

  • Use instanceof and you can never add significant members to subclasses.(
    There is no way to extend an instantiable class and add a value component while preserving the equals contract (Bloch)
  • Use getClass and you violate the Liskov substitution principle

Langers says http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals.html

  • The instanceof test is correct only for final classes or if at least method equals() is final in a superclass. The latter essentially
    implies that no subclass must extend the superclass's state, but can
    only add functionality or fields that are irrelevant for the object's
    state and behavior, such as transient or static fields.

Implementations using the getClass() test on the other hand always
comply to the equals() contract; they are correct and robust. They
are, however, semantically very different from implementations that
use the instanceof test. Implementations using getClass() do not allow
comparison of sub- with superclass objects, not even when the subclass
does not add any fields and would not even want to override equals() .
Such a "trivial" class extension would for instance be the addition of
a debug-print method in a subclass defined for exactly this "trivial"
purpose. If the superclass prohibits mixed-type comparison via the
getClass() check, then the trivial extension would not be comparable
to its superclass. Whether or not this is a problem fully depends on
the semantics of the class and the purpose of the extension.

Summary - use instanceof with final for equals avoids breaking symmetry, and avoids the hibernate "proxy" problem.

Links

  • Liskov and hibernate
  • GetClass vs instanceof in equals

Overriding the equals method vs creating a new method

Overriding the equals method is necessary if you want to test equivalence in standard library classes (for example, ensuring a java.util.Set contains unique elements or using objects as keys in java.util.Map objects).

Note, if you override equals, ensure you honour the API contract as described in the documentation. For example, ensure you also override Object.hashCode:

If two objects are equal according to
the equals(Object) method, then
calling the hashCode method on each of
the two objects must produce the same
integer result.

EDIT: I didn't post this as a complete answer on the subject, so I'll echo Fredrik Kalseth's statement that overriding equals works best for immutable objects. To quote the API for Map:

Note: great care must be exercised if
mutable objects are used as map keys.
The behavior of a map is not specified
if the value of an object is changed
in a manner that affects equals
comparisons while the object is a key
in the map.

How to correctly implement equals(), hashCode() for Tree in Java?

Accordingly, my question is whether I implemented the methods equals/hashCode correctly?

First of all: "what is correct?" ... one could wonder why a Tree should implement equals() and hashCode() in the first place. Especially hashCode() is tricky: the point of that method is (mainly) so you can store the corresponding object in a HashMap/HashSet. But that raises a big red flag: both these classes do not like it, when hashCode() returns different values over time. And that is exactly what your code will be doing: every time you change your tree (adding/removing a node), hashCode() will give a different result.

So we could have a look at what the standard libs do: and there we find JTree ... which doesn't implement both methods! On the other hand, when we look towards AbstractSet (which is the base class for TreeSet), there we find that both methods are implemented and include the members. So both ways seem to be valid.

Coming back to the question: that really depends how you want these two methods to work. Are two trees equal when they have the exact same content (meaning: does the order of children matter)?

Long story short: assuming that you want to ensure that all data is equal, and that all children are equal, and in the same order, then your implementation seems correct.

And yes, that restriction to only check these two attributes makes a lot of sense: when you include the parent link, you immediately get into a recursion that can't be broken.

Finally: you tagged this question with JUnit. This implies that you consider writing tests for your production code. Then these tests should answer your question. Meaning: one approach would be that you sit down and define the contract for these two methods. And then you create a number of test cases that verify all aspects of these contracts. And then your test cases tell you whether your production code meets your contract.

I think that is the crucial point here: there is no universal rule that tells us if/how to implement equals() and hashCode() for a Tree class. You have to look into your requirements if/how to do that. Then you derive tests from that knowledge, which you then you apply in order to verify if a given implementation meets the requirements/contract.

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

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.

Why do I need to override the equals and hashCode methods in Java?

Joshua Bloch says on Effective Java

You must override hashCode() in every class that overrides equals(). Failure to do so will result in a violation of the general contract for Object.hashCode(), which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.

Let's try to understand it with an example of what would happen if we override equals() without overriding hashCode() and attempt to use a Map.

Say we have a class like this and that two objects of MyClass are equal if their importantField is equal (with hashCode() and equals() generated by eclipse)

public class MyClass {
private final String importantField;
private final String anotherField;

public MyClass(final String equalField, final String anotherField) {
this.importantField = equalField;
this.anotherField = anotherField;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((importantField == null) ? 0 : importantField.hashCode());
return result;
}

@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final MyClass other = (MyClass) obj;
if (importantField == null) {
if (other.importantField != null)
return false;
} else if (!importantField.equals(other.importantField))
return false;
return true;
}
}

Imagine you have this

MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");

Override only equals

If only equals is overriden, then when you call myMap.put(first,someValue) first will hash to some bucket and when you call myMap.put(second,someOtherValue) it will hash to some other bucket (as they have a different hashCode). So, although they are equal, as they don't hash to the same bucket, the map can't realize it and both of them stay in the map.


Although it is not necessary to override equals() if we override hashCode(), let's see what would happen in this particular case where we know that two objects of MyClass are equal if their importantField is equal but we do not override equals().

Override only hashCode

If you only override hashCode then when you call myMap.put(first,someValue) it takes first, calculates its hashCode and stores it in a given bucket. Then when you call myMap.put(second,someOtherValue) it should replace first with second as per the Map Documentation because they are equal (according to the business requirement).

But the problem is that equals was not redefined, so when the map hashes second and iterates through the bucket looking if there is an object k such that second.equals(k) is true it won't find any as second.equals(first) will be false.

Hope it was clear



Related Topics



Leave a reply



Submit