Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why should I not use equals with inheritance?

Tags:

java

oop

equals

When I read a Java book, author has said that, when designing a class, it's typically unsafe to use equals() with inheritance. For example:

public final class Date {
    public boolean equals(Object o) {
         // some code here
    }
}

In the class above, we should put final, so other class cannot inherit from this. And my question is, why it is unsafe when allow another class inherit from this?

like image 733
hqt Avatar asked Sep 02 '12 20:09

hqt


People also ask

What does the equals () method is inherited from Object do?

The equals() method compares two objects for equality and returns true if they are equal. The equals() method provided in the Object class uses the identity operator ( == ) to determine whether two objects are equal. For primitive data types, this gives the correct result.

Is equals method inherited in java?

One of the important things that gets inherited is the equals(Object obj) method. This method is used to test if the current object and the passed object called obj are equal.

Why to override equals Object?

We can override the equals method in our class to check whether two objects have same data or not.

Is equal to string Java?

Java String equals() MethodThe equals() method compares two strings, and returns true if the strings are equal, and false if not. Tip: Use the compareTo() method to compare two strings lexicographically.


1 Answers

Because it's hard (impossible?) to make it right, especially the symmetric property.

Say you have class Vehicle and class Car extends Vehicle. Vehicle.equals() yields true if the argument is also a Vehicle and has the same weight. If you want to implement Car.equals() it should yield true only if the argument is also a car, and except weight, it should also compare make, engine, etc.

Now imagine the following code:

Vehicle tank = new Vehicle();
Vehicle bus = new Car();
tank.equals(bus);  //can be true
bus.equals(tank);  //false

The first comparison might yield true if by coincidence tank and bus have the same weight. But since tank is not a car, comparing it to a car will always yield false.

You have few work-arounds:

  • strict: two objects are equal if and only if they have exactly the same type (and all properties are equal). This is bad, e.g. when you subclass barely to add some behaviour or decorate the original class. Some frameworks are subclassing your classes as well without you noticing (Hibernate, Spring AOP with CGLIB proxies...)

  • loose: two objects are equal if their types are "compatible" and they have same contents (semantically). E.g. two sets are equal if they contain the same elements, it doesn't matter that one is HashSet and the other is TreeSet (thanks @veer for pointing that out).

    This can be misleading. Take two LinkedHashSets (where insertion order matters as part of the contract). However since equals() only takes raw Set contract into account, the comparison yields true even for obviously different objects:

    Set<Integer> s1 = new LinkedHashSet<Integer>(Arrays.asList(1, 2, 3));
    Set<Integer> s2 = new LinkedHashSet<Integer>(Arrays.asList(3, 2, 1));
    System.out.println(s1.equals(s2));
    
like image 141
Tomasz Nurkiewicz Avatar answered Sep 27 '22 23:09

Tomasz Nurkiewicz