169
public class Animal {
    public void eat() {}
}

public class Dog extends Animal {
    public void eat() {}

    public void main(String[] args) {
        Animal animal = new Animal();
        Dog dog = (Dog) animal;
    }
}

割り当てDog dog = (Dog) animal;によってコンパイルエラーは生成されませんが、実行時に。が生成されますClassCastException。コンパイラがこのエラーを検出できないのはなぜですか?

4

7 に答える 7

353

キャストを使用することで、基本的にコンパイラに「私を信じてください。私はプロです。私が何をしているのかを知っています。保証はできませんが、このanimal変数は間違いなく犬になります。」

動物は実際には犬ではないので(それは動物です、あなたはそうすることができますAnimal animal = new Dog();、そしてそれは犬になるでしょう)あなたがその信頼に違反したのでVMは実行時に例外をスローします(あなたはコンパイラにすべてが大丈夫だと言ったそしてそれはいいえ!)

コンパイラは、すべてを盲目的に受け入れるよりも少し賢いです。異なる継承階層でオブジェクトをキャストしようとすると(たとえば、犬を文字列にキャストする)、コンパイラは、それが機能しない可能性があることを認識しているため、オブジェクトを返します。

基本的にコンパイラが文句を言うのを止めているだけなので、キャストするたびに、ifステートメント(またはその効果のための何か)でClassCastException使用して、を引き起こさないことを確認することが重要です。instanceof

于 2011-02-01T13:23:35.127 に答える
56

理論的には犬になるAnimal animal ことができるので:

Animal animal = new Dog();

一般的に、ダウンキャストは良い考えではありません。あなたはそれを避けるべきです。これを使用する場合は、次のチェックを含めることをお勧めします。

if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
}
于 2011-02-01T13:13:11.000 に答える
49

この種のClassCastExceptionを回避するために、次の場合:

class A
class B extends A

Aのオブジェクトを受け取るコンストラクターをBで定義できます。このようにして、「キャスト」を実行できます。例:

public B(A a) {
    super(a.arg1, a.arg2); //arg1 and arg2 must be, at least, protected in class A
    // If B class has more attributes, then you would initilize them here
}
于 2012-02-12T21:54:58.513 に答える
29

マイケルベリーによって与えられた答えを詳しく説明します。

Dog d = (Dog)Animal; //Compiles but fails at runtime

ここでは、コンパイラに「信頼してください。実際にオブジェクト dを参照していることはわかっています」と言っていますが、そうではありません。ダウンキャストを実行すると、コンパイラは私たちを信頼するように強制されることを忘れないでくださいDog

コンパイラは、宣言された参照型についてのみ認識します。実行時のJVMは、オブジェクトが実際に何であるかを認識しています。

したがって、実行時のJVMが、がオブジェクトではなくDog d実際にを参照していることを認識した場合、それは言います。ねえ...あなたはコンパイラに嘘をつき、大きな脂肪を投げます。AnimalDogClassCastException

したがって、ダウンキャストしている場合は、失敗instanceofを避けるためにテストを使用する必要があります。

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

今、私たちの頭に浮かぶ質問があります。地獄のコンパイラが最終的にスローするときにダウンキャストを許可しているのはなぜjava.lang.ClassCastExceptionですか?

animal答えは、コンパイラが実行できるのは、2つのタイプが同じ継承ツリーにあることを確認することだけです。したがって、ダウンキャストの前に発生したコードによっては、タイプがである可能性がありdogます。

コンパイラーは、実行時に機能する可能性のあるものを許可する必要があります。

次のコードスニペットについて考えてみます。

public static void main(String[] args) 
{   
    Dog d = getMeAnAnimal();// ERROR: Type mismatch: cannot convert Animal to Dog
    Dog d = (Dog)getMeAnAnimal(); // Downcast works fine. No ClassCastException :)
    d.eat();

}

private static Animal getMeAnAnimal()
{
    Animal animal = new Dog();
    return animal;
}

ただし、キャストが機能しないことをコンパイラーが確信している場合、コンパイルは失敗します。IE異なる継承階層でオブジェクトをキャストしようとした場合

String s = (String)d; // ERROR : cannot cast for Dog to String

ダウンキャストとは異なり、アップキャストは暗黙的に機能します。これは、アップキャストすると、呼び出すことができるメソッドの数が暗黙的に制限されるためです。ダウンキャストとは対照的に、後で、より具体的なメソッドを呼び出すことができます。

Dog d = new Dog(); Animal animal1 = d; // Works fine with no explicit cast Animal animal2 = (Animal) d; // Works fine with n explicit cast

犬は動物であり、動物ができること、犬ができることであるため、上記のアップキャストはどちらも例外なく正常に機能します。しかし、それは真実ではありません。

于 2014-06-23T13:07:57.133 に答える
3

@Caumonsの答えを作成するには:

1つの父親のクラスに多くの子供がいて、そのクラスに共通のフィールドを追加する必要があると想像してください。上記のアプローチを検討する場合は、各子クラスに1つずつ移動し、新しいフィールドのコンストラクターをリファクタリングする必要があります。したがって、そのソリューションは、このシナリオでは有望なソリューションではありません

次に、このソリューションを見てください。

父親は各子供から自己オブジェクトを受け取ることができます。これが父親のクラスです:

public class Father {

    protected String fatherField;

    public Father(Father a){
        fatherField = a.fatherField;
    }

    //Second constructor
    public Father(String fatherField){
        this.fatherField = fatherField;
    }

    //.... Other constructors + Getters and Setters for the Fields
}

これが、その父コンストラクターの1つ、この場合は前述のコンストラクターを実装する必要がある子クラスです。

public class Child extends Father {

    protected String childField;

    public Child(Father father, String childField ) {
        super(father);
        this.childField = childField;
    }

    //.... Other constructors + Getters and Setters for the Fields

    @Override
    public String toString() {
        return String.format("Father Field is: %s\nChild Field is: %s", fatherField, childField);
    }
}

次に、アプリケーションをテストします。

public class Test {
    public static void main(String[] args) {
        Father fatherObj = new Father("Father String");
        Child child = new Child(fatherObj, "Child String");
        System.out.println(child);
    }
}

そしてここに結果があります:

父のフィールドは次のとおりです。父の文字列

子フィールドは次のとおりです。子文字列

これで、子供のコードが壊れることを心配することなく、父親のクラスに新しいフィールドを簡単に追加できます。

于 2018-11-23T05:24:44.857 に答える
1

インスタンスタイプが動物であるため、コードはコンパイルエラーを生成します。

Animal animal=new Animal();

いくつかの理由により、Javaではダウンキャストは許可されていません。詳細はこちらをご覧ください。

于 2011-02-01T13:22:53.430 に答える
1

説明したように、それは不可能です。サブクラスのメソッドを使用する場合は、メソッドをスーパークラスに追加する可能性を評価し(空の場合もあります)、ポリモーフィズムのおかげで必要な動作(サブクラス)を取得するサブクラスから呼び出します。したがって、d.method()を呼び出すと、キャストなしで呼び出しは成功しますが、オブジェクトが犬でない場合は、問題はありません。

于 2015-02-18T14:57:31.783 に答える