在Java中的交叉引用类上重写equals/hashCode会导致StackOverflowerError

在Java中的交叉引用类上重写equals/hashCode会导致StackOverflowerError,java,equals,stack-overflow,cross-reference,Java,Equals,Stack Overflow,Cross Reference,我有两个类,它们代表两个不同的数据库实体。它们的关系在db中为1:m,在类结构中表示如下: public class Company { private List<Employee> employees; public List<Employee> getEmployees() { return employees; } public void setEmployees(List<Employee> emp

我有两个类,它们代表两个不同的数据库实体。它们的关系在db中为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;
    }

}
我希望它能通过,因为1号公司和2号公司都有相同的员工名单,但StackOverflower失败了:

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)
        ...
java.lang.StackOverflower错误
位于java.util.AbstractList$Itr。(AbstractList.java:318)
位于java.util.AbstractList$Itr。(AbstractList.java:318)
位于java.util.AbstractList$ListItr。(AbstractList.java:377)
位于java.util.AbstractList.listIterator(AbstractList.java:315)
位于java.util.AbstractList.listIterator(AbstractList.java:284)
位于java.util.AbstractList.equals(AbstractList.java:502)
位于com.test.Company.equals(Company.java:37)
位于com.test.Employee.equals(Employee.java:35)
位于java.util.AbstractList.equals(AbstractList.java:507)
位于com.test.Company.equals(Company.java:37)
位于com.test.Employee.equals(Employee.java:35)
位于java.util.AbstractList.equals(AbstractList.java:507)
位于com.test.Company.equals(Company.java:37)
位于com.test.Employee.equals(Employee.java:35)
...

我知道失败的原因是类中的交叉引用,因此是equals/hashCode方法。但是我应该如何实现equals/hashCode来避免无限递归呢?

现在,公司的身份完全由员工定义。同样,员工的身份仅由其公司定义。你看到这是如何导致相互逻辑依赖的吗


您需要在代码中打破这种逻辑依赖关系。您如何在逻辑上唯一地识别公司和员工?通常,您可以使用某种有意义的唯一标识符来完成此操作:名称(字符串)、数字(int/long)或一些类似的基本字段组合。

Imho有两种版本可用。我认为公司应该是“领导”阶层,存储员工

  • 版本:在employee equals中,使用“==”检查company上的对象相等性(不是很好)
  • 版本:为您的公司分配一个唯一的ID,并比较employee中只有该公司ID等于

  • hth

    不要比较公司内的员工名单。equals方法。公司是否还有其他有意义的属性,可以用来在equals中进行比较,比如名称?还是股票符号?

    您无意中在
    公司
    员工
    之间建立了递归依赖关系。
    Company#hashCode()
    方法需要计算每个员工的个人hashCode,
    Employee#hashCode()
    方法依赖于公司的hashCode,导致无限递归

    公司对象的哈希代码不应依赖于其中的员工。从某种意义上说,哈希代码是对象的“标识”,当向其添加新员工时,该标识不应更改。员工也一样。员工的身份不应该仅仅因为他/她搬到另一家公司而改变


    您必须根据一些有意义的标识属性重新定义这些方法。您的代码没有显示它,但是
    公司
    员工
    必须有一些其他成员变量,例如名称。基于该属性的hashCode和equals实现。

    谢谢,MΓΓББLL。这些类上还有其他字段(简单字符串,如公司名称、员工姓名等),它们包含在equals/hashCode方法中。我只是在问题中省略了它们,因为它们不会引起任何问题,交叉引用是导致问题的原因。在这种情况下,听起来你应该从
    company\hashCode()
    company\equals()
    方法中省略公司的员工。员工可以由其工作的公司定义,但公司不是由其员工定义的(无论如何,是唯一的)。正如我对schtever的回答所评论的,这将导致两个同名但不同员工的公司相等,这是不对的。那么员工不应该由其工作的公司唯一定义!就像我说的,你必须选择在哪里打破逻辑循环。由您来决定是什么唯一地定义了您的实体。如果名称不足以唯一指定公司,则可以为公司引入单独/独立的标识符字段。你不能两全其美:你需要选择从某个地方开始。谢谢你,MΓΓБСLL。我没有想到单个(或复合)标识符会表示相同类型的两个对象相等。出于某种原因,我认为我需要使用类中的所有字段。也许Eclipse自动生成这些方法需要更加灵活。谢谢你的评论。这些类上还有其他简单的字段。你的建议会导致两个同名但不同员工的公司平等,这是不对的。你能详细解释一下为什么这是不对的吗?您是否会遇到两个同名公司实际上是不同实体的情况?谢谢,schtever。正如我在接受的答案中所评论的那样,我没有想到单个(或复合)标识符会表示相同类型的两个对象相等。出于某种原因,我认为我需要使用该类中的所有字段。我希望拥有相同员工名单的两家公司被视为平等的。仅仅比较身份属性是怎么可能的。不要使用
    equals
    ,为此编写一个单独的
    比较器。谢谢,phloc。我选择选项2。
    
    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));
        }
    
    }
    
    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)
            ...