#java
#java
Вопрос:
Мне нужен способ переопределить метод equals для класса, имеющего циклическую ссылку. Ниже приведен мой класс, РЕДАКТИРОВАТЬ: Удален код для методов получения и установки
class Person implements Serializable{
private String fullName;
private Person friend;
// Getters and setters
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (friend == null) {
if (other.friend != null)
return false;
} else if (!friend.equals(other.friend))
return false;
if (fullName == null) {
if (other.fullName != null)
return false;
} else if (!fullName.equals(other.fullName))
return false;
return true;
}
}
И в клиентском классе у меня есть следующее:
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
p1.setFullName("nkuruza");
p2.setFullName("Another");
p3.setFullName("nkuruza");
p3.setFriend(p2);
p1.setFriend(p2);
p2.setFriend(p1);
Проблема заключается в вызове метода equals в этой ситуации, например, p1.equals(p3) вызывает исключение StackOverflowException.
Как я могу реализовать свой метод equals, не сталкиваясь с этой проблемой?
Заранее благодарю вас.
Комментарии:
1. Зачем вообще переопределять equals()? Реализация по умолчанию, вероятно, является лучшей для такого класса.
2. @JBNizet Я попробовал это, и результат равен false, хотя он должен быть true в случае для
p1.equals(p3)
3. @Nkuruza Как вы определяете равенство для своих
Person
объектов с помощью непрограммных терминов? Когда предполагается, что два разныхPerson
объекта равны друг другу?4. @Progman это было бы, если бы полное имя было таким же, а друг (Person) равен (по этому определению) другому другу
Ответ №1:
Поскольку для p1
‘s friend
установлено значение p2
, а p2
‘s friend
— p1
, ваш equals()
метод заканчивается в бесконечном цикле вызовов между двумя Person
экземплярами:
p1.equals(p2)
вызывает p2.equals(p1)
вызовы p1.equals(p2)
(навсегда — или, скорее, до тех пор, пока не будет достигнут установленный в настоящее время предел размера стека вашей JVM, после чего он выдаст StackOverflowException
)
Решением на данный момент может быть прямое тестирование уникальных атрибутов friend ( fullname
пока является единственным другим атрибутом):
else if(!friend.fullName.equals(other.friend.fullName)
(… конечно, обязательно обновите код для защиты от любых возможных нулевых значений)
Комментарии:
1. Большое вам спасибо за ваш ответ, это определенно решило бы мою проблему с циклической ссылкой, но после прочтения ответа @GhostCat я думаю, что мой класс модели на самом деле не соответствует действительности, и вот где настоящая проблема.
2. @Nkuruza тогда тоже поддержит это 😉 Кроме того, я всегда ценю, когда OP объясняет, почему он принял другой ответ, это хорошие манеры. Отличная работа! 😀
3. @tryman Я поддержал оба ответа, но, к сожалению, у меня недостаточно репутации, однако StackOverflow заверил меня, что голоса up были перекодированы 🙂
4. Ух ты! Да, я полностью согласен с @GhostCat, и дизайн, безусловно, является фундаментальной проблемой здесь. Однако я решил сосредоточиться на механической проблеме, которую вы подняли (бесконечный «цикл вызова»), чтобы убедиться, что вы поняли, почему это происходило в первую очередь.
Ответ №2:
Реальный ответ заключается в моделировании ваших классов в соответствии с реальностью.
На самом деле, каковы реальные аспекты, которые определяют человека?! Это было бы идентификатором, например, именем.
Набор ваших друзей может измениться завтра, но вы все равно останетесь собой!
Итак, другими словами: исключите поле friends из этого сравнения. Еще лучше: подумайте о том, чтобы не записывать это отношение как поле внутри этого класса (и если вообще, это должен быть список, а не отдельный экземпляр). Но, как уже было сказано, в идеале, эта информация выходит за пределы класса person!