私はこの記事を読みました: Javaでの等価メソッドの記述方法 。
基本的に、継承をサポートするequals()メソッドのソリューションを提供します。
Point2D twoD = new Point2D(10, 20);
Point3D threeD = new Point3D(10, 20, 50);
twoD.equals(threeD); // true
threeD.equals(twoD); // true
しかし、それは良い考えでしょうか?これら2つのインスタンスは等しいように見えますが、2つの異なるハッシュコードを持っている可能性があります。それは少し間違っていませんか?
これは、代わりにオペランドをキャストすることでよりよく達成されると思います。
transitivity を壊すため、これは等価であってはなりません。次の2つの式について考えます。
new Point3D(10, 20, 50).equals(new Point2D(10, 20)) // true
new Point2D(10, 20).equals(new Point3D(10, 20, 60)) // true
等式は推移的であるため、次の式も真であることを意味します。
new Point3D(10, 20, 50).equals(new Point3D(10, 20, 60))
しかしもちろん-そうではありません。
だから、あなたのキャストの考えは正しいです-Javaでは、キャストは単に参照の型をキャストすることを意味することを期待してください。ここで本当に必要なのは、Point2D
オブジェクトから新しいPoint3D
オブジェクトを作成する変換メソッドです。これにより、式がより意味のあるものになります。
twoD.equals(threeD.projectXY())
私は、アランJ.ペルリスの 知恵 について考える記事を読むのをやめます:
エピグラム9。10個のデータ構造で10個の関数を処理するよりも、1個のデータ構造で100個の関数を処理する方が適切です。
「平等」を正しくすることは Martin Ordersky Scalaの発明者が夜間に立ち上がることを維持する一種の問題であるという事実は、継承ツリーでequals
をオーバーライドするかどうかについて一時停止を与える必要があります良い考えです。
ColoredPoint
を取得できなかった場合に起こることは、継承を使用してデータ型を増殖させるのではなく、継承を使用してデータ型を増殖させるために、ジオメトリが失敗することです。これは、継承ツリーのルートノードに戻って変更し、equals
を機能させる必要があるにもかかわらずです。 z
とcolor
をPoint
に追加しないのはなぜですか?
Point
とColoredPoint
が異なるドメインで動作するのは、少なくともこれらのドメインが混ざらない場合は十分な理由です。ただし、その場合は、equals
をオーバーライドする必要はありません。 ColoredPoint
とPoint
の同等性の比較は、それらが混ざり合うことが許可されている3番目のドメインでのみ意味があります。そしてその場合、混ざり合ったドメインのどちらか一方または両方からの等価セマンティクスを適用しようとするよりも、その3番目のドメインに合わせて「平等」を調整する方がおそらく良いでしょう。言い換えると、Point
の作成者が考えたとしても、ColoredPoint.equals(pt)
がColoredPoint
のインスタンスに対して失敗しないようにするために、両側から泥が流入する場所のローカルで「同等」を定義する必要があります。 6か月前の午前2時に良いアイデアでした。
古いプログラミングの神々がクラスを使用したオブジェクト指向プログラミングを発明していたとき、彼らは、オブジェクトに対して2つの関係があることを決定しました。「is a」と「has a」です。
これにより、サブクラスが親クラスと異なる問題が部分的に解決されましたが、コードを壊すことなくサブクラスを使用できるようになりました。サブクラスインスタンスは「スーパークラスオブジェクト」であり、直接置き換えることができるため、サブクラスにさらに多くのメンバー関数またはデータメンバーがある場合でも、「は」は、親のすべての機能を実行し、すべてのメンバー。つまり、Point3Dは「is a」Pointであり、Point2Dは「is a」Pointと言うことができます。さらに、Point3DはPoint2Dのサブクラスになる可能性があります。
ただし、クラス間の等価性は問題ドメイン固有であり、上記の例は、プログラムが正しく機能するためにプログラマーが何を必要とするかについてあいまいです。一般に、数学ドメインのルールに従い、比較の範囲をこの場合は2次元に制限するとデータの値は等しくなりますが、すべてのデータメンバーを比較する場合は等しくありません。
したがって、絞り込みの等式の表が得られます。
Both objects have same values, limited to subset of shared members
Child classes can be equal to parent classes if parent and childs
data members are the same.
Both objects entire data members are the same.
Objects must have all same values and be similar classes.
Objects must have all same values and be the same class type.
Equality is determined by specific logical conditions in the domain.
Only Objects that both point to same instance are equal.
一般に、問題ドメインで必要なすべての機能を実行できる最も厳しいルールを選択します。数値の組み込み等式テストは、数学の目的と同じように制限するように設計されていますが、それが目標でない場合は、切り上げ/切り捨て、切り捨て、gt、ltなどを含む、プログラマーがそれを回避する多くの方法があります。タイムスタンプを持つオブジェクトは、多くの場合、それらの生成時間によって比較されるため、各インスタンスは一意でなければならず、比較は非常に具体的になります。
この場合の設計要素は、オブジェクトを比較する効率的な方法を決定することです。場合によっては、すべてのオブジェクトのデータメンバーを再帰的に比較する必要がありますが、データメンバーの多いオブジェクトが多数ある場合は、非常にコストがかかる可能性があります。代替方法は、関連するデータ値のみを比較するか、オブジェクトに関連するデータメンバーのハッシュ値を生成させて、他の類似オブジェクトとの迅速な比較を行い、コレクションをソートおよびプルーニングして比較を高速化し、CPU負荷を軽減し、オブジェクトを許可することです。カリングされるデータが同一であり、単一のオブジェクトへの重複したポインターがその場所に置かれている。
ルールは、hashcode()
をオーバーライドする場合は常にequals()
をオーバーライドし、その逆も同様です。これが良いアイデアかどうかは、使用目的によって異なります。個人的には、同じ効果を得るために別の方法(isLike()
または同様のもの)を使用します。
non-public-facingクラスには、異なるタイプのオブジェクトが同じ情報を表す場合に、それらが互いに「等しい」と見なすことを可能にする等価性テストメソッドがあると便利ですが、Javaは、クラスが互いに偽装できる手段を許可していません。異なる表現を持つ同等のオブジェクトが存在する可能性がある場合は、単一のパブリックラッパータイプを使用するのが良い場合がよくあります。
たとえば、double
値の不変の2D行列をカプセル化するクラスを考えます。 1つの外部メソッドがサイズ1000の単位行列を要求する場合、2番目は対角行列を要求し、1000を含む配列を渡し、3番目は2D行列を要求し、1000x1000配列を渡し、ここで主対角要素はすべて1.0です。他のすべてはゼロであり、3つのクラスすべてに与えられたオブジェクトは内部で異なるバッキングストアを使用する場合があります(最初のサイズは単一のフィールド、2番目は1000要素の配列、3番目は1000要素の配列)。 [3つすべてが1000x1000の不変行列をカプセル化しているので、対角線上に1を持ち、それ以外の場所には0があるため].
異なるバッキングストアタイプの存在を隠すという事実以外にも、ラッパーは比較を容易にするためにも役立ちます。これは、アイテムの等価性のチェックは通常、複数のステップからなるプロセスであるためです。最初の項目が2番目の項目と等しいかどうかを確認してください。それがわからない場合は、最初のものと等しいかどうかがわかっているかどうかを2番目に尋ねます。どちらのオブジェクトもわからない場合は、個々の要素の内容について各配列に質問します(長時間かかる個々のアイテムの比較ルートを決定する前に、他のチェックを追加する場合があります)。
このシナリオの各オブジェクトの等価性テストメソッドは、3つの状態の値を返す必要があることに注意してください(「はい、同等です」、「同等ではありません」、または「わからない」)。したがって、通常の「等しい」方法は適切ではありません。他のオブジェクトについて尋ねられた場合、どのオブジェクトも単に「わからない」と答えることができますが、たとえば、主対角から離れた要素についての単位行列または対角行列をたずねない対角行列は、そのようなタイプ間の比較を大幅に促進します。