Правильный способ реализации равного контракта

У меня есть объект домена Пользователь. Свойства пользователя включают ssoId, имя, адрес электронной почты, createdBy, createdDate и userRole. Из них ssoId должен быть уникальным в том смысле, что ни один из двух пользователей не может иметь одинаковый идентификатор sso. Поэтому мой метод equals проверяет идентификатор sso и возвращает либо true, либо false.

@Override public boolean equals(Object o) {
    if (!(o instanceof User))
      return false;
    return user.getSsoId().equals((User)o.getSsoId());
}

Я чувствую, что это неправильная реализация, хотя она правильна в отношении бизнес-правил. Вышеупомянутая реализация вернет true для двух объектов с одним и тем же идентификатором sso, но с разными значениями для имени или электронной почты или обоих. Должен ли я изменить свой равный контракт, чтобы проверить равенство всех полей? Каково ваше предложение?

Ответ 1

Это (почти) правильно для "технического равенства", но не для "естественного равенства". Чтобы достичь высшего технического равенства, вы также должны проверить рефлексивный o == this. Может случиться так, что объект еще не сохраняется в БД и, тем самым, еще не имеет технического идентификатора. Например.

public class User {

    private Long id;

    @Override
    public boolean equals(Object object) {
        return (object instanceof User) && (id != null) 
             ? id.equals(((User) object).id) 
             : (object == this);
    }

    @Override
    public int hashCode() {
        return (id != null)
             ? (User.class.hashCode() + id.hashCode())
             : super.hashCode();
    }

}

Для "естественного равенства" вы должны сравнить все нетехнические свойства. Для "субъектов реального мира" это все более надежное (но и более дорогое), чем техническое равенство.

public class User {

    private String name;
    private Date birth;
    private int housenumber;
    private long phonenumber;

    @Override
    public boolean equals(Object object) {
        // Basic checks.
        if (object == this) return true;
        if (!(object instanceof User)) return false;

        // Property checks.
        User other = (User) object;
        return Objects.equals(name, other.name)
            && Objects.equals(birth, other.birth)
            && (housenumber == other.housenumber)
            && (phonenumber == other.phonenumber);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, birth, housenumber, phonenumber);
    }

}

Правда, это много кода, когда есть много свойств. Немного достойная IDE (Eclipse, Netbeans и т.д.) Может просто генерировать автогенерацию equals(), hashCode() (а также toString(), getters и seters) для вас. Воспользуйтесь этим. В Eclipse щелкните правой кнопкой мыши и загляните в меню "Источник" (Alt + Shift + S).

См. также:

Ответ 2

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

Ответ 3

То, что вы делаете, кажется прекрасным, и вы не нарушаете никаких правил, которые должны следовать equals.

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

Ответ 4

Это сложное решение.

Это место, в которое я попал, рассматривая хеширование несколько месяцев назад. Я бы посоветовал вам прочитать, что такое хэш, потому что он очень важен для вашего ответа... Я предлагаю вам реализовать какой-то хэш и проверить его равенство.

Существуют разные виды равенства... существует равенство идентичности объекта, равенство данных объекта, равенство всего объекта... вы также можете включить в него информацию аудита.

Дело в том, что "равно" имеет много возможных значений.

Я решил это, реализуя равное строгое равенство во всех полях просто потому, что после запроса вокруг это кажется интуитивным значением равных. Затем я построил метод для других требуемых требований равенства и определил интерфейс для их переноса.

Я бы не тестировал равенство на объекте ==, потому что часто вы тестируете два разных объекта с теми же данными, которые в моей книге равны, несмотря на то, что они ссылаются на разные адреса памяти.