Override equals method

匆匆过客 提交于 2019-12-04 12:57:12

The second piece of code is better:

  • It optimizes for x.equals(x), which isn't necessary for correctness, but is a helpful optimization
  • It copes with x.equals(null) instead of throwing NullPointerException
  • It handles objects of a completely different class without throwing a ClassCastException which yours would (e.g. x.equals("foo"))
  • It requires the exact same type to provide a symmetric relationship; otherwise obj.equals(x) could invoke a different method, giving a different result.

The second version is a safe one, I would say a pedantic one. Your version, instead, could launch a ClassCastException because you are assuming that the runtime type of the variable obj is of type product. Which is not true, that's why you should use this.getClass() != obj.getClass() (you could solve this problem also with instanceof operator).

If I do

Product p = new Product();
p.equals("abc");

I get an exception while I should get false.

In addition it manages the product.equals(null) problem, which should return false as stated in equals contract method in documentation. If you don't care about this and you do, inside you equals:

...
Product p = (Product)obj; // obj is null
obj.id // this throws a NullPointerException

The common idiom used in overriding equals() is

@Override
public boolean equals(Object obj) {
       if (! (obj instanceof Product) ) return false;

       final Product other = (Product) obj;
       if (id != other.id)
           return false;
       return true;
   }

In the second version that you posted:

  • the first if() may be good for optimization only if the following checks are too much expensive. But this is not the case, so that is just redundant code which is evil.
  • That version won't work if you define a Product subclass which does not change the semantics of method equals(). (For example a class which provides some convenience method but no additional internal state to the objects.) This is because of the third if().

Number 2 is right out of Effective Java for the safest way to override equals. 1 has a nullpointer if Object is null and it isn't as optimized as it could be(doesn't check if ojb is a reference to itself)

The solution suggested by 'Joshua Bloch: Effective Java' is (assuming that Product does not have a superclass other than Object):

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (!(obj instanceof Product)) return false;
    final Product other = (Product) obj;
    if (id != other.id) return false;
    return true;
}

Your first solution suffers from two drawbacks:

  • new Product(1).equals(null) throws a NullpointerException although it is specified to return false in Object.equals().
  • new Product(1).equals(new Vector()) throws a ClassCastException although it is specified to return false in Object.equals().

Both of these are remedied by the instance check. The if (this == obj) return true; is often useful for efficiency, but probably not necessary here.

The second solution you posted makes it difficult to write a subclass of Product with good semantics. If you have a subclass

public class SubProduct extends Product {
    SubProduct(int id) {
         super(id);
    }
    ...
}

you will have !new Product(4).equals(new SubProduct(4)). This violates Liskov's susbstitution principle and is often believed to be not so good. If you have a final class the second solutions is the same as the above.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!