Переопределение equals / hashCode в перекрестных ссылочных классах в Java вызывает StackOverflowError - PullRequest
3 голосов
/ 29 марта 2012

У меня есть два класса, которые представляют две разные сущности базы данных.Их отношение равно 1: m в дБ, и оно представлено в структурах классов примерно так:

public class Company {

    private List<Employee> employees;

    public List<Employee> getEmployees() {
        return employees;
    }

    public void setEmployees(List<Employee> employees) {
        this.employees = employees;
    }

}

public class Employee {

    private Company company;

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }

}

Теперь я хочу переопределить equals / hashCode для этих классов.Eclipse генерирует для меня следующий код:

public class Company {

    private List<Employee> employees;

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((employees == null) ? 0 : employees.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Company other = (Company) obj;
        if (employees == null) {
            if (other.employees != null)
                return false;
        } else if (!employees.equals(other.employees))
            return false;
        return true;
    }

}

public class Employee {

    private Company company;

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((company == null) ? 0 : company.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Employee other = (Employee) obj;
        if (company == null) {
            if (other.company != null)
                return false;
        } else if (!company.equals(other.company))
            return false;
        return true;
    }

}

Если я выполню следующий тест:

public class EqualsTest {

    @Test
    public void testEquals() {

        Company company1 = new Company();
        Employee employee1 = new Employee();

        employee1.setCompany(company1);
        company1.setEmployees(Arrays.asList(employee1));

        Company company2 = new Company();
        Employee employee2 = new Employee();

        employee2.setCompany(company2);
        company2.setEmployees(Arrays.asList(employee2));

        assertThat(company1, is(company2));
    }

}

Я ожидаю, что он пройдет, потому что и company1, и company2 имеют одинаковые списки сотрудников, ноэто завершается с StackOverflowError:

java.lang.StackOverflowError
    at java.util.AbstractList$Itr.<init>(AbstractList.java:318)
    at java.util.AbstractList$Itr.<init>(AbstractList.java:318)
    at java.util.AbstractList$ListItr.<init>(AbstractList.java:377)
    at java.util.AbstractList.listIterator(AbstractList.java:315)
    at java.util.AbstractList.listIterator(AbstractList.java:284)
    at java.util.AbstractList.equals(AbstractList.java:502)
    at com.test.Company.equals(Company.java:37)
    at com.test.Employee.equals(Employee.java:35)
    at java.util.AbstractList.equals(AbstractList.java:507)
    at com.test.Company.equals(Company.java:37)
    at com.test.Employee.equals(Employee.java:35)
    at java.util.AbstractList.equals(AbstractList.java:507)
    at com.test.Company.equals(Company.java:37)
    at com.test.Employee.equals(Employee.java:35)
        ...

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

Ответы [ 4 ]

4 голосов
/ 29 марта 2012

Как сейчас, личность компании определяется исключительно ее сотрудниками. Кроме того, личность сотрудника определяется исключительно его компанией. Вы видите, как это приводит к взаимной логической зависимости?

Вам нужно разрушить эту логическую зависимость в вашем коде. Как бы вы логически однозначно идентифицировали компанию и сотрудника? Обычно вы делаете это с каким-то осмысленным уникальным идентификатором: именем (строка), числом (int / long) или какой-либо подобной комбинацией примитивных полей.

2 голосов
/ 29 марта 2012

Имхо доступно 2 версии.Я полагаю, что компания должна быть «ведущим» классом, хранящим сотрудников.

  1. версия: В равных сотрудниках используйте «==» для проверки равенства объектов в компании (не очень приятно)1004 *
  2. версия: назначьте вашей компании уникальный идентификатор и сравните, что только этот идентификатор компании в сотруднике равен

hth

1 голос
/ 29 марта 2012

Вы случайно настроили рекурсивную зависимость между Company и Employee.Метод Company#hashCode() должен вычислять отдельные хеш-коды каждого сотрудника, а метод Employee#hashCode() зависит от хеш-кода компании, что приводит к бесконечной рекурсии.

Хеш-код объекта компании не должен зависеть от сотрудников вЭто.Хеш-код в некотором смысле является «идентификатором» объекта, который не должен изменяться при добавлении в него нового сотрудника.То же самое для сотрудника.Личность сотрудника не должна меняться только потому, что он переходит в другую компанию.

Вам придется переопределить эти методы с точки зрения некоторого значимого атрибута идентичности.Ваш код не показывает его, но и Company, и Employee должны иметь некоторые другие переменные-члены, такие как имя.Основывайте hashCode и равняйте реализации на этом атрибуте.

1 голос
/ 29 марта 2012

Не сравнивайте список сотрудников в методе Company.equals.Существуют ли другие атрибуты компании, которые имеют смысл и могут быть использованы для сравнения в равных, как имя?Или символ акций?

...