Java - equals method in base class and in subclass

2019-01-09 06:40发布

I have a simple base class, which is later extended by many separate classes, which potentially introduce new fields, but not necessarily. I defined an equals method in the base class, but also overriden that for a few subclasses. Is it OK to mix definitions in base/subclasses? In my case it was to avoid code duplication checking the same fields.

标签: java equals
7条回答
啃猪蹄的小仙女
2楼-- · 2019-01-09 06:55

Quite a valid approach. The issue is in one of your subclasses it must retain the definition of equals as is bound by its parent. Else you have a broken equals function, which can cause some very unique scenarios during run time.

查看更多
聊天终结者
3楼-- · 2019-01-09 06:56

Take a look at "Implementing equals() To Allow Mixed-Type Comparison" from Angelika Langer .

Here is a brief explanation of some problems and a possible solution:

The equals contract says (amongst others):

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.

That means you might get problems if your sub class is introducing new fields and you're comparing an object of the base class (or another sub class that doesn't override equals) to an object of this sub class.

Do NOT do the following:

class BaseClass {
    private int field1 = 0;

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof BaseClass) {
            return field1 == ((BaseClass) obj).field1;
        }
        return false;
    }
}

class BadSubClass extends BaseClass {
    private int field2 = 0;

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof BadSubClass) {
            return super.equals(obj) 
                    && field2 == ((BadSubClass) obj).field2;
        }
        return false;
    }
}

because you get

BaseClass baseClass = new BaseClass();
BadSubClass subClass = new BadSubClass();

System.out.println(baseClass.equals(subClass)); // prints 'true'
System.out.println(subClass.equals(baseClass)); // prints 'false'

A possible solution:

Replace the instanceof-check with a class comparison:

obj != null && obj.getClass() == getClass()

With this solution an object of BaseClass will never be equal to an object of any subclass.

If you create another SubClass without an @Override of the equals method, two SubClass-objects can be equal to each other (if the BaseClass.equals check decides so) out of the box, but a SubClass-object will never be equal to a BaseClass-object.

A good implementation could be as follows:

class BaseClass {
    private int field1 = 0;

    @Override
    public boolean equals(Object obj) {
        if (obj != null && obj.getClass() == getClass()) {
            return field1 == ((BaseClass) obj).field1;
        }
        return false;
    }
}

class GoodSubClass extends BaseClass {
    private int field2 = 0;

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof GoodSubClass) {
            return super.equals(obj) && field2 == ((GoodSubClass) obj).field2;
        }
        return false;
    }
}

Please refer to the article mentioned above for more advanced problems and their solutions.

查看更多
小情绪 Triste *
4楼-- · 2019-01-09 06:58

I guess, That's perfect to provide the equals(Object obj) and hashCode() method implementation in super class as Java did. We all know that Java provide the hashCode() and equals(Object obj) method implementation in the base class java.lang.Object, and when ever required we override them in our class.

查看更多
走好不送
5楼-- · 2019-01-09 07:00

No, it's not possible to conform to the equals contract when introducing new fields which are relevant to the equals method. See "Effective Java" by Joshua Bloch for more information.

Edit:

I don't have the book at hand right now, but I think it's ok if the base class is abstract/ cannot be instantiated.

查看更多
倾城 Initia
6楼-- · 2019-01-09 07:01

I think it is perfectly fine as long as you follow eqauls() and hashcode() contracts.

查看更多
姐就是有狂的资本
7楼-- · 2019-01-09 07:03

You could use the super() method to call the method of class that you're extending to prevent any need of code duplication

public class BaseClass {
  public boolean equals(BaseClass other) {
    return (other.getBlahblah() == this.Blahblah && .....);
  }
}

public class DerivedClass extends BaseClass {
  public boolean equals(DerivedClass other) {
    return (super(other) && other.getNewAttribute() == this.NewAttribute.....);
  }
}
查看更多
登录 后发表回答