2011-02-01 59 views
96
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。爲什麼編譯器不能檢測到這個錯誤?顯式轉換的子類

+41

您正在告訴編譯器不檢測錯誤。 – Mauricio 2011-02-01 13:14:02

回答

222

通過使用一個強制轉換,你實際上告訴編譯器「相信我,我是一名專業人員,我知道我在做什麼,我知道雖然你不能保證,但我告訴你這個animal變量肯定會成爲一隻狗。「由於動物實際上並不是一隻狗(它是一種動物,你可以做Animal animal = new Dog();並且它會成爲一隻狗),因此VM在運行時會拋出一個異常,因爲你違反了該信任(你告訴編譯器一切)

編譯器比只是盲目地接受所有東西要聰明一點,如果你嘗試在不同的繼承層次結構中投擲對象(例如將一個Dog轉換爲String),那麼編譯器會拋出它回到你身邊,因爲它知道這絕不可能奏效。

因爲你基本上只是從停止抱怨編譯器,每次你投來檢查,你不會引起ClassCastException是很重要的在if語句(或諸如此類的話。)

38

因爲理論上Animal animal可以是狗:

Animal animal = new Dog(); 

一般而言,向下轉換是不是一個好主意。你應該避免它。如果你使用它,你最好包括檢查:

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

但下面的代碼會生成編譯錯誤Dog dog = new Animal(); (不兼容的類型)。但在這種情況下編譯器識別動物是超級類,而狗是子類。所以這個任務是錯誤的。但是當我們投狗狗=(狗)動物;它接受。請向我解釋這個 – saravanan 2011-02-01 13:19:07

+3

是的,因爲動物是超類。不是每個動物都是狗,對吧?你只能通過它們的類型或它們的超類型來引用類。不是他們的亞型。 – Bozho 2011-02-01 13:31:23

+1

所以編譯器相信我們只在鑄造..是正確的 – saravanan 2011-02-01 13:39:57

1

的代碼生成編譯錯誤,因爲你實例類型是一種動物:

Animal animal=new Animal(); 

向下轉換爲Java不允許有以下幾個原因。 有關詳細信息,請參閱here

30
使用 instanceof

爲了避免這種ClassCastException異常的,如果您有:

class A 
class B extends A 

您可以定義B中的構造函數A的對象這樣我們就可以做到「投」,例如:

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 
} 
17

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

在這裏,你對編譯器說:「相信我。我知道d真的指的是Dog對象」,但事實並非如此。 記得編譯器是被迫當我們做一個沮喪的信任我們。

編譯器只知道聲明的引用類型,JVM在運行時知道對象究竟是什麼。

因此,當在運行時的JVM出這個Dog d實際上是指一個Animal而不是Dog對象,它說。 嘿......你對編譯器撒謊了,並且拋出了一個大胖子ClassCastException

所以,如果你是向下傾斜,你應該使用instanceof測試,以避免搞砸。

現在,一個問題涉及到我們的心靈。爲什麼地獄編譯器在最終拋出一個java.lang.ClassCastException時允許downcast?

答案是,所有的編譯器可以做的是驗證兩種類型在同一個繼承樹,所以這取決於什麼代碼可能已經來過了就垂頭喪氣 ,它可能是animaldog類型。

編譯器必須允許可能在運行時工作的東西。

考慮下面的一小段代碼片段:

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

但是,如果編譯器是確保投將無法工作,編譯將失敗。 I.E.如果試圖投在不同的繼承對象層次結構

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

上述兩個向上轉型將正常工作,沒有任何異常,因爲狗是-A的動物,anithing的動物可以做到,一隻狗能做到。但這不是真的,反之亦然。

1

如上所述,這是不可能的。 如果您想要使用子類的方法,請評估將方法添加到超類(可能爲空)的可能性,並且由於多態性而從子類獲取您想要的行爲(子類)。 所以當你調用d.method()時,調用會成功執行,但是如果對象不是狗,那麼不會有問題

相關問題