Подтвердить что ты не робот

Метод Java - equals в базовом классе и в подклассах

У меня есть простой базовый класс, который позже расширяется многими отдельными классами, которые потенциально могут вводить новые поля, но не обязательно. Я определил метод equals в базовом классе, но также переопределить это для нескольких подклассов. Можно ли смешивать определения в базе/подклассах? В моем случае это было во избежание дублирования кода, проверяющего те же поля.

4b9b3361

Ответ 1

Взгляните на "Внедрение equals(), чтобы разрешить сравнение смешанного типа" от Angelika Langer.

Вот краткое описание некоторых проблем и возможное решение:

В уравнении равенства говорится (среди прочих):

Он симметричен: для любых непустых опорных значений x и y x.equals(y) должен возвращать true тогда и только тогда, когда y.equals(x) возвращает true.

Это означает, что у вас могут возникнуть проблемы, если ваш подкласс вводит новые поля, и вы сравниваете объект базового класса (или другого подкласса, который не переопределяет равные) объекту этого подкласса.

НЕ делать следующее:

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;
    }
}

потому что вы получаете

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

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

Возможное решение:

Замените instanceof -check сравнением классов:

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

При таком решении объект BaseClass никогда не будет равен объекту любого подкласса.

Если вы создаете еще один SubClass без @Override метода equals, два SubClass -объекта могут быть равны друг другу (если проверка BaseClass.equals принимает это решение) из коробки, но a SubClass -объект никогда не будет равен объекту BaseClass.

Хорошая реализация может быть следующей:

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;
    }
}

Пожалуйста, обратитесь к статье, упомянутой выше, для более сложных проблем и их решений.

Ответ 2

Нет, невозможно согласовать равный контракт при введении новых полей, имеющих отношение к методу equals. См. "Эффективная Java" Джошуа Блоха для получения дополнительной информации.

Edit:

У меня сейчас нет книги, но я думаю, что это нормально, если базовый класс является абстрактным/не может быть создан.

Ответ 3

Я думаю, что это прекрасно, если вы следуете eqauls() и hashcode().

Ответ 4

Вы можете использовать метод super() для вызова метода класса, который вы распространяете, чтобы предотвратить необходимость дублирования кода

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.....);
  }
}

Ответ 5

Достаточно правильный подход. Проблема заключается в одном из ваших подклассов: должен сохранять определение equals как связанное с его родителем. Кроме того, у вас есть сломанная функция равенства, которая может вызвать некоторые очень уникальные сценарии во время выполнения.

Ответ 6

Я предполагаю, что это идеально, чтобы обеспечить реализацию метода equals(Object obj) и hashCode() в super class как Java. Мы все знаем, что Java предоставляет реализацию метода hashCode() and equals(Object obj) в базовом классе java.lang.Object, и когда когда это требуется, мы override их в нашем class.

Ответ 7

В то время как следующее не обрабатывает каждый случай, я нашел его вполне практичным. Я использовал это много раз, когда у меня есть SuperClass и SubClass в игре. Я не хочу сравнивать их, но я также не хочу повторно реализовывать все значения SuperClass equals() для SubClass. Он обрабатывает:

  • a.equals(b) == b.equals(a)
  • Не дублирует код сравнения полей
  • Легко обобщается для любой глубины подкласса
  • Subclass.equals(SuperClass) == false
  • Superclass.equals(SubClass) == false

Пример кода

// implement both strict and asymmetric equality
class SuperClass {
   public int f1;
   public boolean looseEquals(Object o) {
      if (!(o instanceof SuperClass)) return false;
      SuperClass other = (SuperClass)o;
      return f1 == other.f1;
   }
   @Override public boolean equals(Object o) {
      return looseEquals(o) && this.getClass() == o.getClass();
   }
}
class SubClass extends SuperClass {
   public int f2;
   @Override public boolean looseEquals(Object o) {
      if (!super.looseEquals(o)) return false;
      if (!(o instanceof SubClass)) return false;
      SubClass other = (SubClass)o;
      return f2 == other.f2;
   }
   // no need to override equals()
}

Ответ 8

Если вы не напишите свой код правильно, это создаст серьезную проблему, называемую асимметрия (нарушает контракт на равенство), поэтому давайте посмотрим наши варианты.

Лучшая практика - стратегия одинаковых классов. Если B является подклассом A и каждый класс имеет свой собственный метод equals, реализованный используя ту же стратегию классов, то класс B должен быть объявлен окончательным, чтобы предотвратить введение асимметричной определение равных в любом будущем подклассе B.

Вопрос. Что, если мы не хотим сделать финал B?

Используйте Композицию вместо Наследования. Всякий раз, когда классы B и A, где B является подклассом A, требуют различных методов равенства, использование композиции вместо наследования является хорошей стратегией, и если сделать финал класса B не вариант, это единственный безопасный способ обращаться с равными.

Как?

 public class A{
public boolean equals(Object ob){
//write your code here
}
}

class B{
A a= new A();
public B(A a){
this.a= a;
}
public boolean equals(Object ob){
//...write your code here
if(!((B)ob).a).equals(a)) return false;
//...write your code here

}
}