Реализовать equals() для класса с циклической ссылкой Java

#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!