2012-04-30 60 views
2

下面的代碼片段將導致運行時:的Java鑄造導致運行時錯誤而不是編譯錯誤

class Vehicle { 
    public void printSound() { 
     System.out.print("vehicle"); 
    } 
} 

class Car extends Vehicle { 
    public void printSound() { 
     System.out.print("car"); 
    } 
} 

class Bike extends Vehicle { 
    public void printSound() { 
     System.out.print("bike"); 
    } 
} 

public class Test { 
    public static void main(String[] args) { 
     Vehicle v = new Car(); 
     Bike b = (Bike) v; 

     v.printSound(); 
     b.printSound(); 
    } 
} 

我的問題是:爲什麼是導致運行時錯誤,但不編譯錯誤?編譯器是否應該知道'v'已經是'Car'並且不能被轉換成'Bike'?

回答

9

從理論上講,這將是可能的編譯器自言自語地說:「v是局部變量,分配是一個0​​在任何時候之前試圖投地Bike它改變它的價值, Car無法成功轉換爲Bike,因此這是一個錯誤。「

但是,我知道沒有Java編譯器會爲你做分析。在最簡單的情況下它確實是值得的。相反,其行爲是編譯器會看到劇集,以及可以將Vehicle投射到Bike的原因,因此它允許。

一般來說,這就是演員表達的意思:它告訴編譯器,即使這個任務可能失敗,你也很確定它不會。作爲允許編譯代碼的交換,您承擔運行時異常的風險。

0

No. v是一個Vehicle它可能會將它投射到Bike。編譯器的工作不是找出每個對象的實際運行時類型(特別是因爲有時這是不可能的)。

1

對象的類型轉換髮生在運行時間,使編譯器doen't承認它

4

從超類鑄件可以工作,所以它被允許(編譯時)。從一個完全不同的類鑄件是不允許的,例如:

Integer a = 1; 
String b = (String)a; // compile error 
String b = (String)(Object)a; // runtime error 
0

的Java的語義說,這必須導致運行時錯誤。在這種情況下,可以查看代碼並確定它在運行時肯定會拋出錯誤,但編譯器如何知道ClassCastException不是您想要的?

像IntelliJ和Eclipse這樣的編輯器可以(而且確實)注意到這些類型的錯誤並警告你,但是Java的規則說這是必須編譯的合法代碼。

2

對於

R r = /* some code to initialize "r" */ 
T t = (T) r; 

Java語言規範說:

如果R是一個普通的類(不是數組類):

  • 如果T是一個類類型,那麼R必須與T的子類或T的子類相同,否則會引發運行時異常。
  • 如果T是接口類型,那麼R必須實現接口T,否則會引發運行時異常。
  • 如果T是一個數組類型,則會引發運行時異常。
+0

不錯,簡單的解釋。以供將來參考JLS SE8 https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html – georger

0

這是因爲你已經定義的變量v作爲Car運行時錯誤。您無法將Car轉換爲Bike

編譯器不會檢查這種類型的值賦值,因爲編譯器通常不檢查語義。