Best practice to choose fields for equals() implementation

前端 未结 5 1962
栀梦
栀梦 2021-02-05 13:43

When writing unit-tests, I often face the situation when equals() for some object in tests -- in assertEquals -- should work differently from how it wo

5条回答
  •  失恋的感觉
    2021-02-05 14:07

    what are best practices to implement equals, semantically, not technically.

    In Java the equals method really should be considered to be "identity equals" because of how it integrates with Collection and Map implementations. Consider the following:

     public class Foo() {
        int id;
        String stuff;
     }
    
     Foo foo1 = new Foo(10, "stuff"); 
     fooSet.add(foo1);
     ...
     Foo foo2 = new Foo(10, "other stuff"); 
     fooSet.add(foo2);
    

    If Foo identity is the id field then the 2nd fooSet.add(...) should not add another element to the Set but should return false since foo1 and foo2 have the same id. If you define Foo.equals (and hashCode) method to include both the id and the stuff fields then this might be broken since the Set may contain 2 references to the object with the same id field.

    If you are not storing your objects in a Collection (or Map) then you don't have to define the equals method this way, however it is considered by many to be bad form. If in the future you do store it in a Collection then things will be broken.

    If I need to test for equality of all fields, I tend to write another method. Something like equalsAllFields(Object obj) or some such.

    Then you would do something like:

    assertTrue(obj1.equalsAllFields(obj2));
    

    In addition, a proper practice is to not define equals methods which take into account mutable fields. The problem also gets difficult when we start talking about class hierarchies. If a child object defines equals as a combination of its local fields and the base class equals then its symmetry has been violated:

     Point p = new Point(1, 2);
     // ColoredPoint extends Point
     ColoredPoint c = new ColoredPoint(1, 2, Color.RED);
     // this is true because both points are at the location 1, 2
     assertTrue(p.equals(c));
     // however, this would return false because the Point p does not have a color
     assertFalse(c.equals(p));
    

    Some more reading I would highly recommend is the "Pitfall #3: Defining equals in terms of mutable fields" section in this great page:

    How to Write an Equality Method in Java

    Some additional links:

    • Implementing hashCode() and equals()
    • Graceful Blog - Values, Equals, and Hashcodes

    Oh, and just for posterity, regardless of what fields you choose to compare to determine equality, you need to use the same fields in the hashCode calculation. equals and hashCode must be symmetric. If two objects are equals, they must have the same hash-code. The opposite is not necessarily true.

提交回复
热议问题