Базовый класс не определяет равных, но подкласс должен.Как реализовать? - PullRequest
3 голосов
/ 17 февраля 2011

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

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

Было бы неправильно считать сравнение полей базового класса в моем подклассе равным? Зачем ?

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

Ответы [ 4 ]

6 голосов
/ 17 февраля 2011

Да, вы можете сделать это, не будет никаких проблем с вызовом геттеров в вашем суперклассе, так как вы можете определять равенство любым способом, каким хотите, если вы соблюдаете контракт:

Reflexive : x.equals (x) должен возвращать true.

Симметричный : x.equals (y) == y.equals (x)

Переходный : x.equals (y) && y.equals (z) => x.equals (z)

Последовательный : множественные вызовы x.equals (y) последовательно возвращает истину или ложь, если между вызовами не изменяются x или y.

И контракт равного хеш-кода:

Равные объекты должны иметь равные хеш-коды1024 *

http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#equals(java.lang.Object)

2 голосов
/ 17 февраля 2011

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

0 голосов
/ 18 февраля 2011

Нет ничего особенно плохого в переопределении equals, и, конечно, нет ничего плохого в доступе к закрытым методам через их геттеры, если это то, что вам нужно сделать.

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

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

equals(Object o) { return this == o;}

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

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

0 голосов
/ 17 февраля 2011

Не должно быть проблем, при условии, что вы также переопределите hashCode() и при условии, что рассматриваемые свойства неизменны .

Метод equals(), который иногда возвращает истину, а иногда возвращает ложь, в зависимости от того, кто-то вызвал setXXX() в то же время, является очень опасной вещью - его нельзя использовать для большей части того, что equals() хорошо подходит для определения членства в коллекции.

Если equals() действительно то, что вам нужно - для работы с другими API, такими как java.util.Collections, - тогда я предлагаю переопределить мутаторы для выдачи UnsupportedOperationException. Если нет, ваш метод equals() будет ненадежным. См. Например: Как использовать два числа в качестве ключа карты

Если вы не можете - например, если бы мутаторы были final - тогда я бы даже не пытался создавать подклассы. Вместо этого я скопировал бы значения из объекта «суперкласса» в мои собственные поля, а затем выбросил оригинал. (Если вы храните его, даже как частное поле, вы не можете рассчитывать на то, что кто-то другой не будет ссылаться на него и изменять его.) Если вам нужен ваш «подкласс» для взаимодействия с API, для которых требуется «суперкласс», затем включите метод asXXX(), который генерирует объект «суперкласса» на лету.

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

...