我有两个类,表示两个不同的数据库实体。它们的关系在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;
}
}
现在我想在这些类上重写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));
}
}
我希望它能通过,因为公司1和公司2都有相同的员工名单,但它失败了StackOverflow错误:
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)
...
我知道这个失败的原因是类中的交叉引用,因此equals/hashCode方法。但是我应该如何实现equals/hashCode来避免无限递归呢?
现在,公司的身份只由员工定义。同样,员工的身份完全由公司来定义。你看到这是如何导致相互逻辑依赖的吗?
您需要打破代码中的逻辑依赖关系。你如何从逻辑上唯一地识别一家公司和一名员工?通常,您会使用某种有意义的唯一标识符来执行此操作:名称(字符串)、数字(int/long)或一些类似的基元字段组合。
Imho有两个版本可用。我认为公司应该是"领导"阶层,存储员工。
- version:在employee equals中,使用"=="检查公司的对象平等(不太好)
- 版本:为您的公司分配一个唯一的ID,并比较只有员工中的公司ID等于
hth
不要比较Company.equals方法中的员工列表。公司是否还有其他有意义的属性,可以用来在平等的范围内进行比较,比如名字?还是股票符号?
您无意中在Company
和Employee
之间建立了递归依赖关系。Company#hashCode()
方法需要计算每个员工的个人哈希代码,而Employee#hashCode()
方法取决于公司的哈希代码,导致无限递归。
公司对象的哈希代码不应依赖于其中的员工。哈希代码在某种意义上是对象的"身份",当添加新员工时,该身份不应更改。employee也是如此。员工的身份不应该仅仅因为他/她跳槽到另一家公司而改变。
您必须根据一些有意义的身份属性来重新定义这些方法。您的代码没有显示它,但Company
和Employee
都必须有一些其他成员变量,例如名称。基于该属性的hashCode和equals实现。