Javaでの変数のキャスト


84

キャストのしくみを教えてもらえませんか?いつ行うべきかは理解していますが、実際にはどのように機能するかはわかりません。プリミティブデータ型については部分的に理解していますが、オブジェクトのキャストに関しては、それがどのように機能するかを理解していません。

Object型のオブジェクトを突然キャストしてMyType(単なる例)、すべてのメソッドを取得するにはどうすればよいですか?



回答:


182

Javaでのキャストは魔法ではありません。コンパイラーに、タイプAのオブジェクトは実際にはより具体的なタイプBであると伝え、他の方法では得られなかったBのすべてのメソッドにアクセスできるようにします。キャストを実行するときに、魔法や変換を実行していません。基本的に、コンパイラに「信頼してください。私が何をしているのかを知っています。この行のこのオブジェクトが実際には<キャストを挿入することを保証できます。ここに入力>。」例えば:

Object o = "str";
String str = (String)o;

上記は問題ありませんが、魔法ではありません。oに格納されているオブジェクトは実際には文字列であるため、問題なく文字列にキャストできます。

これがうまくいかない可能性がある2つの方法があります。まず、完全に異なる継承階層の2つのタイプ間でキャストしている場合、コンパイラはあなたがばかげていることを認識し、あなたを止めます。

String o = "str";
Integer str = (Integer)o; //Compilation fails here

次に、それらが同じ階層にあるが、それでも無効なキャストである場合、ClassCastException実行時にaがスローされます。

Number o = new Integer(5);
Double n = (Double)o; //ClassCastException thrown here

これは本質的に、コンパイラの信頼に違反したことを意味します。オブジェクトが特定のタイプであることを保証できるとおっしゃいましたが、そうではありません。

なぜキャストが必要なのですか?さて、最初に、より一般的なタイプからより具体的なタイプに移行するときにのみ必要です。たとえば、Integerから継承するNumberので、をIntegerとして保存する場合は問題ありませんNumber(すべての整数が数値であるため)。ただし、逆に移動する場合はキャストが必要です。すべての数値が整数であるとは限りません(同様に)我々が持っている整数としてDoubleFloatByteLong、など)をプロジェクトまたはJDKでただ一つのサブクラスがありますとしても、誰かが簡単にあなたが保証をしたんので、あなたはそれが単一、当然の選択だと思う場合でも、別のものを作成して、それを配布することができ!!

キャストの使用に関しては、一部のライブラリでまだその必要性が見られます。Java-5より前は、すべてのコレクションがオブジェクトの追加と、コレクションから取得した結果のキャストに取り組んでいたため、コレクションやその他のさまざまなクラスで頻繁に使用されていました。ただし、ジェネリックの出現により、キャストの使用の多くはなくなりました-ClassCastExceptionsの可能性なしに、はるかに安全な代替手段を提供するジェネリックに置き換えられました(実際、ジェネリックをクリーンに使用し、警告なしでコンパイルする場合、 ClassCastExceptionが発生しないことが保証されています。)


ご説明をいただき、ありがとうございます。私がこれを正しく理解している場合、おそらく私は理解していません。オブジェクトをキャストするとき、このメモリアドレス上のオブジェクトがこれらのメソッドなどに応答する方法を知っていることをコンパイラに伝えているだけなので、私を否定しないでください。あれは正しいですか?
user626912 2011年

1
@ user626912種類-メモリアドレスの観点からは考えないでください。指定されたオブジェクトがインターフェイスに準拠しているため、指定されたメソッドが実装されていることをコンパイラに伝えるだけではありません(同じメソッドでまったく異なるオブジェクトを作成でき、キャストが必ずしも機能するとは限りません)。あるタイプのオブジェクトは実際にはより具体的なタイプであるため、そのより具体的なオブジェクトで使用可能なメソッドを使用できます。まだ読んでいない場合は、ポリモーフィズムについて読んでください。いくつかのことを明確にするのに役立つ場合があります。
マイケルベリー

「より一般的なタイプからより具体的なタイプに移行する場合にのみ必要です」というあなたの発言には疑問があります。((Object)gpsLastLoc.getLatitude())。getClass()。getSimpleName()は、ランタイムで「double」名を返します。これは、より具体的なタイプからより一般的なタイプへのキャストの使用例です。
フルーツ

1
@林果皞この場合、プリミティブを宣伝するためにキャストを使用しているだけです。これは、より一般的なタイプに進むことと同じではなく、非常に奇妙な方法です。より通常の(より良い)方法はですDouble.valueOf(gpsLastLoc.getLatitude()).getClass().getSimpleName()。どちらの場合も、プリミティブのクラスを動的に取得する必要はありません。doubleプリミティブをgetLatitude()返す場合は、Doubleオブジェクトにプロモートすることが常にわかっているからです。
マイケルベリー

コードが「コンパイラの信頼に違反している」というあなたの言い方が大好きです。(Ps。タイプミスがあります。「you've」ではなく「you're」と書きました。)
Jason L.

7

実際、キャストは常に機能するとは限りません。オブジェクトがinstanceofキャスト先のクラスでない場合はClassCastException、実行時に取得します。


5

aStringをaにキャストしたいとしますFile(はい、意味がありません)。Fileクラスはクラスの子でも親でもないため、直接キャストすることはできませんString(コンパイラが文句を言います)。

しかし、あなたはあなたを唱えられるStringObjectあるため、StringあるObjectObject親です)。次にFile、ファイルはObject。であるため、このオブジェクトをにキャストできます。

したがって、コンパイル時の入力の観点からは、すべての操作は「合法」ですが、実行時に機能するという意味ではありません。

File f = (File)(Object) "Stupid cast";

コンパイラは、意味がない場合でもこれを許可しますが、次の例外を除いて実行時にクラッシュします。

Exception in thread "main" java.lang.ClassCastException:
    java.lang.String cannot be cast to java.io.File

3

参照のキャストは、instanceofそのタイプの場合にのみ機能します。ランダムな参照をキャストすることはできません。また、についてもっと読む必要がありますCasting Objects

例えば

String string = "String";

Object object = string; // Perfectly fine since String is an Object

String newString = (String)object; // This only works because the `reference` object is pointing to a valid String object.

3

正しい方法はこれです:

Integer i = Integer.class.cast(obj);

この方法cast()は、コンパイル時のキャストよりもはるかに安全な代替手段です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.