web-dev-qa-db-ja.com

なぜinstanceOfの後にキャストするのですか?

以下の例(私のコースパックから)では、Squareインスタンスc1に他のオブジェクトp1の参照を与えたいと思いますが、これら2つが互換性のあるタイプである場合に限ります。

if (p1 instanceof Square) {c1 = (Square) p1;}

ここで私が理解していないのは、最初にp1が実際にSquareであることを確認してから、それをキャストすることです。 Squareの場合、なぜキャストするのですか?

答えは見かけのタイプと実際のタイプの違いにあると思いますが、それでも混乱しています...

編集:
コンパイラはどのように処理しますか:

if (p1 instanceof Square) {c1 = p1;}

Edit2:
instanceof見かけのタイプではなく実際のタイプをチェックする問題はありますか?そして、キャストがapparentタイプを変更するということですか?

ありがとう、

JDelage

25
JDelage

Squareのインスタンスは、継承チェーンの上位の型にいつでも割り当てることができることに注意してください。次に、より具体的でないタイプをより具体的なタイプにキャストすることができます。その場合、キャストが有効であることを確認する必要があります。

Object p1 = new Square();
Square c1;

if(p1 instanceof Square)
    c1 = (Square) p1;
13
Justin Niessner

コンパイラーは、ブロック内にいるため、オブジェクトのタイプのチェックが正常に行われたとは推測しません。オブジェクトを別の型として参照することをコンパイラーに通知するには、明示的なキャストが必要です。

if (p1 instanceof Square) {
    // if we are in here, we (programmer) know it's an instance of Square
    // Here, we explicitly tell the compiler that p1 is a Square
    c1 = (Square) p1;
}

C#では、1回の呼び出しでチェックとキャストを行うことができます。

c1 = p1 as Square;

これにより、p1が正方形にキャストされ、キャストが失敗した場合、c1はnullに設定されます。

12
jjnguy

古いコードは正しく機能しません

impield cast featureは結局正当化されますが、下位互換性のために、このFRをJavaに実装するのに問題があります。

これを参照してください:

public class A {
    public static void draw(Square s){...} // with impield cast
    public static void draw(Object o){...} // without impield cast
    public static void main(String[] args) {
        final Object foo = new Square();
        if (foo instanceof Square) {
            draw(foo);
        }
    }
}

現在のJDKは、2番目に宣言されたメソッドの使用法をコンパイルします。このFRをJavaで実装すると、最初のメソッドを使用するようにコンパイルされます。

6
Peter Rader

あるオブジェクトが箱に収まるかどうかを測定することと、実際には置く箱に入れることには違いがあります。 instanceofは前者であり、キャストは後者です。

5
Mark Peters

この特定の シンタックスシュガー はまだ言語に追加されていないためです。 Java 7に提案されたと思いますが、入っていないようです プロジェクトコイン

5
Bozho

例えば。 Object型としてp1を渡すと、コンパイラはそれが実際にSquareのインスタンスであることを認識しないため、メソッドなどにアクセスできなくなります。 ifは、特定の型をチェックしてtrue/falseを返すだけですが、変数p1の型は変更されません。

1
Jules

変数p1は、それが開始したタイプが何であれ、Shapeとしましょう。 p1はシェイプであり、現在の内容がたまたま正方形であるかどうかに関係なく、シェイプのみです。 Squareではside()を呼び出すことができますが、Shapeでは呼び出すことができません。タイプがShapeである変数p1を介して問題のエンティティを識別している限り、変数のタイプが原因で、そのエンティティでside()を呼び出すことはできません。 Javaの型システムの仕組みでは、正方形であることがわかったときにp1.side()を呼び出すことができれば、常に p1.side()を呼び出すことができます。ただし、p1は正方形の形状だけでなく(たとえば)円の形状も保持できます。p1が円を保持しているときにp1.side()を呼び出すとエラーになります。したがって、Shapeを表す別の変数が必要です。これは、タイプがSquareであるSquareです。だからキャストが必要です。

1
Carl Manaster

テストは、実行時にClassCastExceptionsを防ぐために行われます。

Square c1 = null;
if (p1 instanceof Square) {
   c1 = (Square) p1;
} else {
   // we have a p1 that is not a subclass of Square
}

あなたが絶対に肯定的であるならば、それはp1Squareであるため、テストする必要はありません。しかし、これはプライベートメソッドに任せてください...

0
Andreas_D

c1Squareのタイプとして宣言されている場合は、キャストが必要です。 Objectとして宣言されている場合、キャストは必要ありません。

0
Boris Pavlović

不快ではありませんが、コンパイラに何をしたいのかを伝える必要があります。これは、コンパイラが何をしようとしているのかを推測するための代替手段になるためです。確かに、「オブジェクトの型をチェックしている場合、明らかに、それはその型にキャストしたいという意味に違いない」と思うかもしれません。しかし、誰が言いますか?多分それはあなたがしていることであり、多分そうではありません。

確かに、のような単純なケースでは

if (x instanceof Integer)
{
  Integer ix=(Integer) x;
  ...

私の意図はかなり明白です。またはそれは?たぶん私が本当に欲しいのは:

if (x instanceof Integer || x instanceof Double)
{
  Number n=(Number) x;
... work with n ...

または私が書いた場合はどうなりますか:

if (x instanceof Integer || x instanceof String)

コンパイラが次に何をすることを期待しますか? xに対してどのタイプを想定する必要がありますか?

Instanceofは廃止されている、またはその他の点で悪い考えであるというコメントを再確認してください。間違いなく誤用される可能性があります。私は最近、元の作者が6つのクラスを作成し、それらがすべてページとページの長さであるが、互いに同一であることが判明したプログラムに取り組みました。それらを持っている唯一の明白な理由は、彼が「xinstanceofclassA」と「 x instanceof classB "など。つまり、彼はクラスを型フラグとして使用しました。クラスを1つだけにして、さまざまなタイプの列挙型を追加する方がよいでしょう。しかし、非常に良い用途もたくさんあります。おそらく最も明白なのは次のようなものです:

public MyClass
{
  int foo;
  String bar;
  public boolean equals(Object othat)
  {
    if (!(othat instanceof MyClass))
      return false;
    MyClass that=(MyClass) othat;
    return this.foo==that.foo && this.bar.equals(that.bar); 
  }
  ... etc ...
}

Instanceofを使用せずにそれをどのように行いますか?パラメータをObjectではなくMyClass型にすることができます。しかし、それを汎用オブジェクトで呼び出す方法すらありません。これは多くの場合非常に望ましいことです。実際、たとえば、文字列と整数の両方をコレクションに含めたい場合や、異なる型の比較で単純にfalseを返したい場合があります。

0
Jay