Javaでのダウンキャスト


179

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");
  }
}

9
コードとエラーの例のスニペットは、概念を学習しようとしている人々にとって、これをより良い質問にします。
Bob Cross

3
ボブのコメントに+1。質問はまったく明確ではありません。
Jon Skeet、

上記の例は、velocityreviews.com / forums / t151266-downcasting-problem.htmlから取得したもので、すでにいくつかの良い答えがあります。
PhiLho 2008

2
@PhiLho-Joelの主な目的は、すべてのすばらしい質問と回答を1つの共通の傘の下に収めることでした。質問/コード/回答がすでに他のサイトに投稿されているかどうかは関係ありません。要点がわかったら、Joelのポッドキャストを聞いてください。
全能性

これを編集して、コードスニペットがすべて4つのスペースでインデントされるようにしてください。これでフォーマットが修正されます。
スリム

回答:


298

ダウンキャストは、実行時に成功する可能性がある場合に許可されます。

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

キャスト(この最後のキャストなど)が実行時に失敗するClassCastExceptionと、a がスローされます。

他の場合にはそれはうまくいきます:

Object o = "a String";
String s = (String) o; // this will work, since o references a String

キャストがまったく成功しないため、一部のキャストはコンパイル時に許可されないことに注意してください。

Integer i = getSomeInteger();
String s = (String) i; // the compiler will not allow this, since i can never reference a String.

Object o = new Object(); String s = (String) o;それは私にとってはうまく機能しています。
Asif Mushtaq

@UnKnown:すべきではありません。実際にそのバージョンをコンパイルして実行したことを再確認し、それを再現できる場合は、別の質問を投稿してくださいSSCCEを使用)。
Joachim Sauer

@JoachimSauerそのバージョンの意味は?私は、Java 8.使用しています
アシフMushtaq

1
@UnKnown:投稿したコードは実行されません(コンパイルされますが、実行時に例外がスローされます)。これらのコメントはそれをデバッグするスペースではありません。別の質問を投稿してください。
Joachim Sauer

実行時にキャストがどのように失敗しますか?ターゲットオブジェクト参照をnullに設定しますか?例外を投げますか?
CygnusX1 2017年

17

あなたの例を使用して、あなたは行うことができます:

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();
}

抽象クラスが複数のクラスによって拡張されているときにinstanceofの重要性を学び、抽象クラスタイプを参照しながら、それらのクラスの排他的なメソッドを使用したいと思いました。instanceofを使用しないと、クラスキャスト例外が発生しました
Tarun

16

これは静的に型付けされたすべての言語に当てはまると思います:

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であるとしましょう。これがStringであると想定しても意味がなく、予期しない結果になるため、ランタイムチェックと例外があり、ランタイム環境に何か問題があることを通知する必要があります。

実用的には、より一般的なクラスで機能するコードを記述できますが、それがどのサブクラスであるかがわかっていて、そのように扱う必要がある場合は、サブクラスにキャストしてください。典型的な例は、Object.equals()のオーバーライドです。Carのクラスがあると仮定します。

@Override
boolean equals(Object o) {
    if(!(o instanceof Car)) return false;
    Car other = (Car)o;
    // compare this to other and return
}

私は「本当に」という言葉が好きです。あなたの投稿を編集して、より
見やすく

5

あなたが提供したコードが実行時に機能しないことは誰でも理解できます。これは、式new A()がtypeのオブジェクトにはなり得ないことがわかっているためBです。

しかし、それはコンパイラがそれを見る方法ではありません。コンパイラーがキャストが許可されているかどうかをチェックしているときには、次のようになっています。

variable_of_type_B = (B)expression_of_type_A;

そして、他の人が実証したように、そのようなキャストは完全に合法です。右の式は、型のオブジェクトに非常によく評価できますB。コンパイラーはそれAを認識しB、サブタイプの関係があるため、コードの「式」ビューを使用すると、キャストが機能する場合があります。

コンパイラは、オブジェクトタイプが実際に持つオブジェクトタイプを正確に知っている場合、特別なケースを考慮しませんexpression_of_type_A。これは、静的型をAと見なし、動的型がを含む可能性がある、Aまたはその子孫であると見なすだけです。AB


3

この場合、ランタイムで実行できない場合にJavaがダウンキャストを許可するのはなぜですか?

これは、キャストが成功するかどうかをコンパイラーがコンパイル時に知る方法がないためだと思います。あなたの例では、キャストが失敗することを確認するのは簡単ですが、それがそれほど明確ではない場合もあります。

たとえば、タイプB、C、およびDがすべてタイプAを拡張し、メソッドpublic A getSomeA()がランダムに生成された数に応じてB、C、またはDのいずれかのインスタンスを返すとします。コンパイラーは、このメソッドによって返される正確な実行時の型を知ることができないため、後で結果をにキャストしたB場合、キャストが成功(または失敗)するかどうかを知る方法はありません。したがって、コンパイラはキャストが成功すると想定する必要があります。


2

@オリジナルポスター-インラインコメントを参照してください。

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");
    }
}

2

ダウンキャストは、アップキャストされたオブジェクトを処理する場合に機能します。アップキャスト:

int intValue = 10;
Object objValue = (Object) intvalue;

したがって、objValueキャストさintれたオブジェクトはなので、この変数は常にダウンキャストできますInteger

int oldIntValue = (Integer) objValue;
// can be done 

ので、しかし、objValueオブジェクトがある、それはへのキャストすることはできませんStringのでintにキャストすることはできませんString


0

ダウンキャストは、私がこれをいつも使用している次のコードスニペットで非常に役立ちます。したがって、ダウンキャストが有用であることを証明します。

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を使用すると、ダウンキャストコードをコンパイルして、間違ったことが行われていることを信頼できます。それでも人間が間違いを犯した場合、それは実行時に捕捉されます。


Javaで非ジェネリックコレクションを使用することはvoid*、C ++ のポインターと同等です。それは私にはまったく良い考えのようには聞こえません。
Jezor 2016

0

以下の例を考えてみましょう

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内のメソッドの存在について知ることができます


AOneはやや混乱しています。クラス名をDog and Animalなどに変更することを検討してください
Kartik Chugh '26

0

Javaでダウンキャストを行い、実行時の例外を回避するには、次のコードの参照を使用します。

if (animal instanceof Dog) {
  Dog dogObject = (Dog) animal;
}

ここでは、Animalが親クラスで、Dogが子クラスです。
instanceofは、参照変数に特定のタイプのオブジェクト参照が含まれているかどうかを確認するために使用されるキーワードです。


0

オブジェクトの変換をダウンキャストすることはできません。のみ

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();
        }
    }

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