2

これらのインターフェイスの値オブジェクトを実装しています。

interface FooConsumer
{
    public void setFoo(FooKey key, Foo foo);
    public Foo getFoo(FooKey key);
}

// intent is for this to be a value object with equivalence based on 
// name and serial number
interface FooKey
{
    public String getName();
    public int getSerialNumber();
}

そして、私が読んだことから(たとえば、インターフェイスで「equals」を強制し、インターフェイスtoString()、equals()、およびhashCode()で)推奨事項は、抽象基本クラスを提供することです。

abstract class AbstractFooKey
{
    final private String name;
    final private int serialNumber
    public AbstractFooKey(String name, int serialNumber)
    {
       if (name == null)
          throw new NullPointerException("name must not be null");
       this.name = name;
       this.serialNumber = serialNumber;
    }
    @Override public boolean equals(Object other)
    {
       if (other == this)
          return true;
       if (!(other instanceof FooKey))
          return false;
       return getName().equals(other.getName() 
          && getSerialNumber() == other.getSerialNumber()
          && hashCode() == other.hashCode();       // ***
    }
    @Override public int hashCode()
    {
       return getName().hashCode() + getSerialNumber()*37;
    }
}

私の質問は、ここで追加した最後のビットと、 が実装されているがサブクラス化されていないクラスのインスタンスであるAbstractFooKey.equals(x)値で呼び出される状況に対処する方法です。これを処理する方法がわかりません。一方で、等価のセマンティクスは、名前とシリアル番号が等しいことに依存する必要があるように感じますが、Object.equals() の契約を満たすためには、hashCodes も等しくなければならないようです。xFooKeyAbstractFooKey

私はすべきですか:

  1. 本当に怠惰で、マークされた行を忘れてください***
  2. 怠けて、私が持っているものを保管してください
  3. オブジェクトfalseequals()_ other_AbstractFooKey
  4. 本当に厳密になり、インターフェイス FooKey を取り除き、最終的なクラスに置き換えますか?
  5. 他の何か?
4

3 に答える 3

2

契約の一部として必要なセマンティクスを文書化します。

理想的には、実際には最終的な単一の実装があり、この特定の目的のためのインターフェースの必要性を否定します。タイプのインターフェースが必要な理由は他にもあるかもしれません。

の契約要件Objectは実際には以下からのものhashCodeです:If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

計算に含める必要はありません。むしろhashCode、計算に関係するequalsすべてのプロパティを含める必要があります。この場合、との両方を単純に比較します。equalshashCodeserialNumbernameequalshashCode

あなたがそれを複雑にする本当の理由がない限り、それを単純にしてください。

最後の不変のクラスから始めます。

インターフェイスが必要な場合は、一致するインターフェイスを作成し、セマンティクスとデフォルトの実装を文書化します。

于 2012-10-09T15:13:08.797 に答える
1

equals と hashmap には、厳密な契約があります。

  1. Reflexive - オブジェクトがそれ自体と等しくなければならないことを意味します。equals メソッドを意図的にオーバーライドして、別の動作をさせる場合を除きます。

  2. 対称- あるクラスのオブジェクトが別のクラス オブジェクトと等しい場合、他のクラス オブジェクトはこのクラス オブジェクトと等しくなければならないことを意味します。言い換えれば、あるオブジェクトが別のオブジェクトと等しいかどうかを一方的に判断することはできません。2 つのオブジェクト、したがってそれらが属するクラスは、それらが等しいかどうかを双方向で決定する必要があります。彼らは両方とも同意する必要があります。

  3. Transitive - 最初のオブジェクトが 2 番目のオブジェクトと等しく、2 番目のオブジェクトが 3 番目のオブジェクトと等しい場合を意味します。この場合、最初のオブジェクトは 3 番目のオブジェクトと等しくなります。言い換えれば、2 つのオブジェクトが等しいことに同意し、対称原理に従う場合、それらの 1 つが異なるクラスの別のオブジェクトと同様の契約を持つことを決定することはできません。これら 3 つのクラスのさまざまな順列について、3 つすべてが一致し、対称原則に従う必要があります。

  4. 一貫性- 2 つのオブジェクトが等しい場合、それらが変更されない限り、それらは等しいままでなければならないことを意味します。同様に、それらが等しくない場合、それらが変更されない限り、それらは等しくないままでなければなりません。変更は、それらのいずれかまたは両方で行われる可能性があります。

  5. null 比較- インスタンス化可能なクラス オブジェクトが null と等しくないことを意味します。したがって、引数として null が渡された場合、equals メソッドは false を返す必要があります。null が引数として渡された場合、equals メソッドの実装が false を返すことを確認する必要があります。

hashCode() のコントラクト:

  1. 同じ実行中の一貫性- 最初に、オブジェクトが equals メソッドに影響を与えるように変更されていない限り、アプリケーションの同じ実行中の複数の呼び出しに対して、hashCode メソッドによって返されるハッシュ コードが一貫して同じである必要があると述べています。

  2. Hash Code と Equals の関係- コントラクトの 2 番目の要件は、equals メソッドで指定された要件の hashCode 対応物です。同じ関係を強調するだけです。等しいオブジェクトは同じハッシュ コードを生成する必要があります。ただし、3 番目の点は、等しくないオブジェクトが別個のハッシュ コードを生成する必要がないことを詳述しています。

(出典: Technofundo: Equals and Hash Code )

ただし、 equals でinstanceofを使用するのは適切ではありません。Joshua Bloch は、Effective Java でこれを詳しく説明しており、equals 実装の有効性に関するあなたの懸念は有効です。ほとんどの場合、instanceof の使用に起因する問題は、基本クラスの子孫に関連して使用されると、契約の推移性の部分に違反することになります (equals 関数が final にされない限り)。

(ここでできるよりも少し詳しく説明します: Stackoverflow: .equals() を生成するときに instanceof よりも getClass() を優先する理由はありますか? )

また読む:

于 2012-10-09T15:06:52.367 に答える