Javaではアップキャストが許可されていますが、ダウンキャストではコンパイルエラーが発生します。
コンパイルエラーはキャストを追加することで削除できますが、実行時にとにかく壊れます。
この場合、実行時に実行できない場合にJavaがダウンキャストを許可するのはなぜですか?
この概念の実用的な用途はありますか?
public class demo {
public static void main(String a[]) {
B b = (B) new A(); // compiles with the cast,
// but runtime exception - Java.lang.ClassCastException
}
}
class A {
public void draw() {
System.out.println("1");
}
public void draw1() {
System.out.println("2");
}
}
class B extends A {
public void draw() {
System.out.println("3");
}
public void draw2() {
System.out.println("4");
}
}
ダウンキャスティングは、実行時に成功する可能性がある場合に許可されます。
Object o = getSomeObject(),
String s = (String) o; // this is allowed because o could reference a String
場合によっては、これは成功しません。
Object o = new Object();
String s = (String) o; // this will fail at runtime, because o doesn't reference a String
他では動作します:
Object o = "a String";
String s = (String) o; // this will work, since o references a String
実行時にキャスト(この最後のキャストなど)が失敗すると、 ClassCastException
がスローされます。
一部のキャストは、決して成功しないため、コンパイル時に許可されないことに注意してください。
Integer i = getSomeInteger();
String s = (String) i; // the compiler will not allow this, since i can never reference a String.
あなたの例を使用すると、次のことができます:
public void doit(A a) {
if(a instanceof B) {
// needs to cast to B to access draw2 which isn't present in A
// note that this is probably not a good OO-design, but that would
// be out-of-scope for this discussion :)
((B)a).draw2();
}
a.draw();
}
これは静的に型付けされたすべての言語に当てはまると思います。
String s = "some string";
Object o = s; // ok
String x = o; // gives compile-time error, o is not neccessarily a string
String x = (String)o; // ok compile-time, but might give a runtime exception if o is not infact a String
タイプキャストは効果的に、これがキャストクラスへの参照であると想定し、そのように使用します。ここで、oが本当に Integerであるとしましょう。これが理にかなっていて予期しない結果をもたらすと仮定すると、ランタイムチェックと例外がランタイム環境に通知される必要があります。違う。
実際の使用では、より一般的なクラスで動作するコードを記述できますが、サブクラスが何かを知っていて、そのように扱う必要がある場合は、サブクラスにキャストします。典型的な例は、Object.equals()のオーバーライドです。 Carのクラスがあると仮定します。
@Override
boolean equals(Object o) {
if(!(o instanceof Car)) return false;
Car other = (Car)o;
// compare this to other and return
}
提供されたコードは実行時に機能しないことがすべてわかります。これは、式new A()
がneverがB
型のオブジェクトになる可能性があることを知っているためです。
しかし、それはコンパイラがそれを見る方法ではありません。コンパイラがキャストが許可されているかどうかを確認するまでに、次のように表示されます。
variable_of_type_B = (B)expression_of_type_A;
そして、他の人が示したように、そのようなキャストは完全に合法です。右側の式は、B
型のオブジェクトに非常によく評価できます。コンパイラーは、A
とB
にサブタイプの関係があることを認識しているため、コードの「式」ビューではキャストが機能する可能性があります。
コンパイラは、正確にexpression_of_type_A
が実際に持っているオブジェクト型を知っている場合、特別な場合を考慮しません。静的型をA
として認識し、動的型はA
またはA
を含むB
の子孫であると見なします。
この場合、実行時に実行できない場合にJavaがダウンキャストを許可するのはなぜですか?
これは、キャストが成功するかどうかをコンパイラがコンパイル時に知る方法がないためだと思います。たとえば、キャストが失敗するのは簡単ですが、それほど明確ではない場合もあります。
たとえば、タイプB、C、およびDがすべてタイプAを拡張し、public A getSomeA()
メソッドが、ランダムに生成された数値に応じてB、C、またはDのインスタンスを返すとします。コンパイラーは、このメソッドによってどの正確なランタイム型が返されるかを知ることができないため、後で結果をB
にキャストした場合、キャストが成功(または失敗)するかどうかを知る方法はありません。したがって、コンパイラはキャストが成功すると想定する必要があります。
@オリジナルポスター-インラインコメントを参照してください。
public class demo
{
public static void main(String a[])
{
B b = (B) new A(); // compiles with the cast, but runtime exception - Java.lang.ClassCastException
//- A subclass variable cannot hold a reference to a superclass variable. so, the above statement will not work.
//For downcast, what you need is a superclass ref containing a subclass object.
A superClassRef = new B();//just for the sake of illustration
B subClassRef = (B)superClassRef; // Valid downcast.
}
}
class A
{
public void draw()
{
System.out.println("1");
}
public void draw1()
{
System.out.println("2");
}
}
class B extends A
{
public void draw()
{
System.out.println("3");
}
public void draw2()
{
System.out.println("4");
}
}
ダウンキャストは、アップキャストされたオブジェクトを処理する場合に機能します。アップキャスティング:
int intValue = 10;
Object objValue = (Object) intvalue;
したがって、キャストされたオブジェクトはobjValue
であるため、このint
変数は常にInteger
にダウンキャストできます。
int oldIntValue = (Integer) objValue;
// can be done
ただし、objValue
はObjectであるため、String
をint
にキャストできないため、String
にキャストできません。
オブジェクトのダウンキャスト変換はできません。 「DownCasting1 _downCasting1 =(DownCasting1)((DownCasting2)downCasting1);」のみ可能です
class DownCasting0 {
public int qwe() {
System.out.println("DownCasting0");
return -0;
}
}
class DownCasting1 extends DownCasting0 {
public int qwe1() {
System.out.println("DownCasting1");
return -1;
}
}
class DownCasting2 extends DownCasting1 {
public int qwe2() {
System.out.println("DownCasting2");
return -2;
}
}
public class DownCasting {
public static void main(String[] args) {
try {
DownCasting0 downCasting0 = new DownCasting0();
DownCasting1 downCasting1 = new DownCasting1();
DownCasting2 downCasting2 = new DownCasting2();
DownCasting0 a1 = (DownCasting0) downCasting2;
a1.qwe(); //good
System.out.println(downCasting0 instanceof DownCasting2); //false
System.out.println(downCasting1 instanceof DownCasting2); //false
System.out.println(downCasting0 instanceof DownCasting1); //false
DownCasting2 _downCasting1= (DownCasting2)downCasting1; //good
DownCasting1 __downCasting1 = (DownCasting1)_downCasting1; //good
DownCasting2 a3 = (DownCasting2) downCasting0; // Java.lang.ClassCastException
if(downCasting0 instanceof DownCasting2){ //false
DownCasting2 a2 = (DownCasting2) downCasting0;
a2.qwe(); //error
}
byte b1 = 127;
short b2 =32_767;
int b3 = 2_147_483_647;
// long _b4 = 9_223_372_036_854_775_807; //int large number max 2_147_483_647
long b4 = 9_223_372_036_854_775_807L;
// float _b5 = 3.4e+038; //double default
float b5 = 3.4e+038F; //Sufficient for storing 6 to 7 decimal digits
double b6 = 1.7e+038;
double b7 = 1.7e+038D; //Sufficient for storing 15 decimal digits
long c1 = b3;
int c2 = (int)b4;
//int 4 bytes Stores whole numbers from -2_147_483_648 to 2_147_483_647
//float 4 bytes Stores fractional numbers from 3.4e−038 to 3.4e+038. Sufficient for storing 6 to 7 decimal digits
float c3 = b3; //logic error
double c4 = b4; //logic error
} catch (Throwable e) {
e.printStackTrace();
}
}
}
ダウンキャストは、私がこれを常に使用する次のコードスニペットで非常に役立ちます。したがって、ダウンキャストが有用であることを証明します。
private static String printAll(LinkedList c)
{
Object arr[]=c.toArray();
String list_string="";
for(int i=0;i<c.size();i++)
{
String mn=(String)arr[i];
list_string+=(mn);
}
return list_string;
}
リンクリストに文字列を格納します。リンクリストの要素を取得すると、オブジェクトが返されます。要素を文字列(または他のクラスオブジェクト)としてアクセスするには、ダウンキャストが役立ちます。
Javaを使用すると、間違ったことをしていると信じてダウンキャストコードをコンパイルできます。それでも人間が間違いを犯した場合、実行時にキャッチされます。
以下の例を考えてください
public class ClastingDemo {
/**
* @param args
*/
public static void main(String[] args) {
AOne obj = new Bone();
((Bone) obj).method2();
}
}
class AOne {
public void method1() {
System.out.println("this is superclass");
}
}
class Bone extends AOne {
public void method2() {
System.out.println("this is subclass");
}
}
ここで、サブクラスBoneのオブジェクトを作成し、それをスーパークラスAOne参照に割り当てましたが、スーパークラス参照は、サブクラスのメソッドmethod2、つまりコンパイル時にBoneを認識しません。したがって、スーパークラスのこの参照をサブクラス参照にダウンキャストする必要があります。結果の参照は、サブクラス(Bone)のメソッドの存在について知ることができます