187

私は Eclipse を使用して と を生成.equals().hashCode()ています。「型を比較す​​るには 'instanceof' を使用する」というオプションがあります。デフォルトでは、このオプションはチェックされておらず、.getClass()型の比較に使用されます。私が好むべき理由はあります.getClass()instanceof

使用しない場合instanceof:

if (obj == null)
  return false;
if (getClass() != obj.getClass())
  return false;

使用instanceof:

if (obj == null)
  return false;
if (!(obj instanceof MyClass))
  return false;

私は通常、instanceofオプションをチェックしてから、「if (obj == null)」チェックを外します。( null オブジェクトは常に失敗するため、冗長instanceofです。) それが悪い考えである理由はありますか?

4

11 に答える 11

185

Josh Blochはあなたのアプローチを支持します:

私がこのアプローチを好む理由instanceofは、このアプローチを使用するとgetClass、オブジェクトが同じクラス、同じランタイム タイプの他のオブジェクトとのみ等しいという制限があるからです。クラスを拡張し、いくつかの無害なメソッドを追加する場合、サブクラスのオブジェクトがスーパークラスのオブジェクトと等しいかどうかを確認します。オブジェクトがすべての重要な側面で等しい場合でも、それらが等しくないという驚くべき答え。実際、これはリスコフの置換原理の厳密な解釈に違反しており、非常に驚​​くべき動作につながる可能性があります。Java では、ほとんどのコレクション (HashTableなど) は equals メソッドに基づいています。スーパー クラスのメンバーをキーとしてハッシュ テーブルに配置し、サブクラスのインスタンスを使用してそれを検索すると、それらが等しくないため見つかりません。

この SO answerも参照してください。

これについては、 Effective Javaの第 3 章でも説明されています。

于 2009-02-27T20:21:55.507 に答える
107

を使用する場合instanceofequals実装finalを行うと、メソッドの対称コントラクトが保持されますx.equals(y) == y.equals(x)。制限があると思われる場合finalは、オブジェクトの同等性の概念を注意深く調べて、オーバーライドする実装がObjectクラスによって確立されたコントラクトを完全に維持していることを確認してください。

于 2009-02-27T21:11:51.907 に答える
68

アンジェリカ・ランガース・シークレット・オブ・イコールは、ジョシュ・ブロックやバーバラ・リスコフなど、いくつかの一般的で有名な例についての長く詳細な議論で、それらのほとんどにいくつかの問題を発見しました。彼女はまたinstanceofvsに入りgetClassます。それからのいくつかの引用

結論

equals()の実装の任意に選択された4つの例を分析した後、何を結論付けますか?

まず第一に、equals()の実装で型の一致のチェックを実行する方法は2つあります。クラスでは、instanceof演算子を使用してスーパークラスオブジェクトとサブクラスオブジェクトの混合型比較を行うことができます。または、クラスはgetClass()テストを使用して異なる型のオブジェクトを等しくないものとして扱うことができます。上記の例は、getClass()を使用したequals()の実装が、instanceofを使用した実装よりも一般的に堅牢であることをうまく示しています。

instanceofテストは、finalクラスに対してのみ、または少なくともメソッドequals()がスーパークラスでfinalである場合にのみ正しくなります。後者は基本的に、サブクラスがスーパークラスの状態を拡張する必要がないことを意味しますが、オブジェクトの状態や動作に関係のない機能やフィールド(一時フィールドや静的フィールドなど)のみを追加できます。

一方、getClass()テストを使用する実装は、常にequals()コントラクトに準拠しています。それらは正しく、堅牢です。ただし、これらは、instanceoftestを使用する実装とは意味的に非常に異なります。getClass()を使用した実装では、サブクラスがフィールドを追加せず、equals()をオーバーライドしたくない場合でも、サブクラスオブジェクトとスーパークラスオブジェクトを比較できません。このような「些細な」クラス拡張は、たとえば、まさにこの「些細な」目的のために定義されたサブクラスにdebug-printメソッドを追加することです。スーパークラスがgetClass()チェックによる混合型の比較を禁止している場合、些細な拡張機能はそのスーパークラスと比較できません。これが問題であるかどうかは、クラスのセマンティクスと拡張の目的に完全に依存します。

于 2009-02-27T21:52:39.080 に答える
68

使用する理由は、コントラクトgetClassの対称性を確保するためです。equalsequals の JavaDocs から:

これは対称的です: null 以外の参照値 x および y について、y.equals(x) が true を返す場合に限り、x.equals(y) は true を返す必要があります。

instanceof を使用することで、対称でなくなる可能性があります。例を考えてみましょう: Dog は Animal を拡張します。equalsアニマルズはアニマルのinstanceofチェックをします。Dog'sequalsはDogのinstanceofチェックをします。Animal aと Dog dを指定します (他のフィールドは同じ):

a.equals(d) --> true
d.equals(a) --> false

これは対称性に違反します。

equal の契約に厳密に従うには、対称性を確保する必要があるため、クラスが同じである必要があります。

于 2009-02-27T21:34:21.120 に答える
27

これは宗教的な議論のようなものです。どちらのアプローチにも問題があります。

  • instanceofを使用すると、サブクラスに重要なメンバーを追加することはできません。
  • getClassを使用すると、リスコフの置換原則に違反します。

Blochには、 Effective JavaSecondEditionに関連するもう1つのアドバイスがあります。

  • 項目17:相続のための設計と文書化または相続の禁止
于 2009-02-27T22:15:14.000 に答える
24

間違っている場合は訂正してください。ただし、インスタンスが比較対象のクラスのサブクラスではないことを確認する場合は、 getClass() が役立ちます。そのような状況で instanceof を使用すると、次の理由でそれを知ることができません。

class A { }

class B extends A { }

Object oA = new A();
Object oB = new B();

oA instanceof A => true
oA instanceof B => false
oB instanceof A => true // <================ HERE
oB instanceof B => true

oA.getClass().equals(A.class) => true
oA.getClass().equals(B.class) => false
oB.getClass().equals(A.class) => false // <===============HERE
oB.getClass().equals(B.class) => true
于 2011-10-26T05:03:34.267 に答える
5

そのクラスのみが一致することを確認したい場合は、 を使用しますgetClass() ==。サブクラスを一致させたい場合instanceofは、必要です。

また、instanceof は null とは一致しませんが、null と比較しても安全です。したがって、null チェックを行う必要はありません。

if ( ! (obj instanceof MyClass) ) { return false; }
于 2009-02-27T20:24:48.323 に答える
5

特定のクラスのサブクラスがその親と等しいかどうかを考慮するかどうかによって異なります。

class LastName
{
(...)
}


class FamilyName
extends LastName
{
(..)
}

LastName を FamilyName と比較したいので、ここでは「instanceof」を使用します。

class Organism
{
}

class Gorilla extends Organism
{
}

ここでは 'getClass' を使用します。これは、クラスが既に 2 つのインスタンスが同等ではないことを示しているためです。

于 2009-02-27T20:25:19.757 に答える
3

どちらの方法にも問題があります。

サブクラスがIDを変更する場合は、実際のクラスを比較する必要があります。そうしないと、対称プロパティに違反します。たとえば、異なるタイプのPersonsは、同じ名前であっても同等と見なされるべきではありません。

ただし、一部のサブクラスはIDを変更しないため、を使用する必要がありますinstanceof。たとえば、不変のShapeオブジェクトがたくさんある場合、Rectangle長さと幅が1のaは単位に等しくなりSquareます。

実際には、前者の方が当てはまる可能性が高いと思います。通常、サブクラス化はあなたのアイデンティティの基本的な部分であり、あなたが一つの小さなことをすることができることを除いてあなたの親とまったく同じであることはあなたを平等にすることはありません。

于 2009-02-27T22:06:10.420 に答える
3

instanceofは、同じクラスまたはそのサブクラスのインスタンスに対して機能します

これを使用して、オブジェクトがクラスのインスタンス、サブクラスのインスタンス、または特定のインターフェイスを実装するクラスのインスタンスであるかどうかをテストできます。

ArryaList と RoleList は両方ともListのインスタンスです

その間

getClass() == o.getClass()は、両方のオブジェクト ( this と o ) がまったく同じクラスに属している場合にのみ true になります。

したがって、比較する必要があるものに応じて、どちらかを使用できます。

あなたのロジックが「1つのオブジェクトが他のオブジェクトと等しいのは、それらが両方とも同じクラスである場合のみ」である場合、「等しい」を選択する必要があります。これはほとんどの場合だと思います。

于 2009-02-27T20:33:29.707 に答える
-1

実際に instanceof は、オブジェクトがどこかの階層に属しているかどうかをチェックします。例: Car オブジェクトは Vehical クラスに属します。したがって、「Vehical の new Car() インスタンス」は true を返します。また、「new Car().getClass().equals(Vehical.class)」は false を返しますが、Car オブジェクトは Vehical クラスに属しますが、別の型として分類されます。

于 2015-12-01T13:27:32.197 に答える