2012-03-29 10 views
3

2つの異なるデータベースエンティティを表す2つのクラスがあります。彼らの関係は1:このようなデシベル、それは、クラス構造体で表現された何かでM:Javaの相互参照クラスでequals/hashCodeをオーバーライドすると、StackOverflowErrorが発生します。

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; 
    } 

} 

は今、私はこれらのクラスの等号/のhashCodeをオーバーライドしたいです。私は次のテストを実行した場合

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; 
    } 

} 

:Eclipseは私のために以下のコードを生成します

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

今のように、会社のアイデンティティは従業員によってのみ定義されます。同様に、従業員の身元は会社によってのみ定義されます。それがどのように相互依存関係につながるのか見ていますか?

コード内の論理的な依存関係を解除する必要があります。あなたはどのようにして会社と従業員を論理的に一意に識別しますか?通常は、名前(文字列)、数値(int/long)、または同様のプリミティブフィールドの組み合わせなど、一意の一意の識別子を使用します。

+0

ありがとう、MДΓΓБДLL。これらのクラスには他のフィールド(社名、従業員名などのsimle文字列)があり、equals/hashCodeメソッドに含まれています。問題の原因となっているものをクロスリファレンスと呼びます。 – parxier

+0

その場合、Company#hashCode()およびCompany#equals()メソッドから会社の従業員を省略する必要があるようです。従業員は会社の定義することができますが、従業員によって定義される会社はありません。 –

+1

名前が同じだが従業員の異なる2つの企業が同等になるというschteverの答えにコメントしたので、それは正しくありません。 – parxier

2

Imho 2つのバージョンがあります。私は会社が従業員を抱えている「先導的な」クラスでなければならないと考えています。

  1. バージョン:従業員に等しく、バージョン
  2. (非常に良いではない)の会社上のオブジェクトの等価性をチェックするために、「==」を使用します。あなたの会社のユニークなIDを割り当てるとしているだけでその会社のIDを比較従業員は

HTH

+0

私はオプション#2に行くでしょう。 – parxier

1

Company.equalsメソッド内で従業員のリストを比較しないでくださいに等しいです。意味があり、名前のように平等の範囲内で比較を実行するために使用できる他の属性がありますか?ストックシンボルですか?

+0

これらのクラスには他の簡単なフィールドがあります。あなたの提案は、同名の従業員の異なる2つの会社が同等であることを引き起こしますが、それは正しくありません。 – parxier

+0

なぜそれが正しいのではないかについて詳しく説明できますか?同じ名前の2つの企業が実際に異なるエンティティになる状況がありますか? – schtever

+0

ありがとう、schtever。私が受け入れられた答えにコメントしたので、同じタイプの2つのオブジェクトが等しいことを示す単一(または複合)識別子については考えませんでした。何らかの理由で私はクラスのすべてのフィールドを使用する必要があると思っていました。 – parxier

1

CompanyEmployeeの間の再帰的な依存関係を誤って設定しました。 Company#hashCode()メソッドは、すべての従業員の個々のハッシュコードを計算する必要があり、Employee#hashCode()メソッドは、会社のハッシュコードに依存し、無限再帰につながります。

企業オブジェクトのハッシュコードは、その中の従業員に依存すべきではありません。ハッシュコードは、ある意味ではオブジェクトの「同一性」であり、新しい従業員が追加されたときに変更すべきではありません。従業員も同じです。従業員の身元は、別の会社に転勤するだけで変わるべきではありません。

意味のあるID属性の観点から、これらのメソッドを再定義する必要があります。コードには表示されませんが、CompanyEmployeeの両方に、名前などの他のメンバー変数が必要です。 hashCodeと等価の実装をその属性に基づいて構築します。

+0

私は従業員の同じリストを持つ2つの企業が同等と見なされることを望みます。アイデンティティ属性だけを比較すると、どのように可能になりますか? – parxier

+0

'equals'を使わないでください。その目的のために別の' Comparator'を書いてください。 –

関連する問題