2009-10-09 74 views
0

當我編譯下面的代碼,我只看到在運行時它說 錯誤「無法投型‘Foo1’的對象鍵入‘foo2的’」爲什麼只有在將基類對象轉換爲dervied類對象時出現運行時錯誤?

爲什麼編譯器不會顯示在此錯誤編譯時間?

public void Start() 
{ 
    Foo1 objFoo1 = new Foo1(); 
    Foo2 objFoo2 = (Foo2)objFoo1; 

    //objFoo1.FooA = 10; 
    //Console.WriteLine(objFoo2.FooA); 

} 

public class Foo1 {} 
public class Foo2 : Foo1 {} 
+0

對不起。我無法正確格式化代碼塊。爲什麼有猜測? – csharpbaby 2009-10-09 04:42:06

+0

好的。我使用代碼格式化程序。現在代碼看起來很好:) – csharpbaby 2009-10-09 04:43:07

+0

對不起,在閱讀你的第一條評論之後,試圖幫助格式化代碼。 – David 2009-10-09 04:44:19

回答

2

編譯器是不夠聰明,知道objFoo1確實是一個簡單的Foo1。它沒有足夠深入地分析你的源代碼來確定它。它看到的全部是objFoo1Foo1,Foo2是從Foo1派生的,因此演員陣容是合法的。相反,如果將其更改爲(int) objFoo1,它會在編譯時被炸燬,因爲編譯器可能會看到無法將Foo1變成整數。

想象一下,這兩個聲明之間有1000行代碼,其中許多代碼執行各種分配objFoo1。編譯器將不得不做大量的努力來試圖確定objFoo1中究竟是什麼對象。總的來說,它並不總是能夠這樣做。對於編譯器來說,僅僅採用面值的靜態類型信息並假定objFoo1是某種類型的Foo1對象,但是沒有更多。這樣它就不會拒絕你的簡單測試程序,而是接受更復雜的測試程序。

在這種情況下,拒絕你的代碼也不是編譯器的工作。儘管公然有些牽強,但你有意試圖通過非法轉換生成ClassCastException。這很不尋常,但不是非法的。

沿着這些線路,還有其他情況下,編譯器的靜態代碼分析可能會被愚弄。這將無法編譯:

return; 
System.out.println("hello world!"); // compile error - unreachable code 

雖然這將編譯罰款,即使它是語義相同:

if (true) return; 
System.out.println("hello world!"); 
-1

它因爲編譯器正在服從您的指令進行強制轉換。然後它將允許編譯代碼。

Foo2 objFoo2 = (Foo2)objFoo1; 

如果你不強迫它,它會給你一個編譯錯誤。

Foo2 objFoo2 = objFoo1; // kaboom 

無論如何,真正的原因是,你的實例化對象Foo1,這是foo2的基地,而不是foo2的。

,如果你做這種方式,它會工作

Foo1 foo1 = new Foo2(); 
Foo2 foo2 = foo1 as Foo2; 
1

沒有編譯時錯誤,因爲它是可能你的代碼可能會因爲工作objFoo1實際上可能是Foo2類型 - 編譯器根本不知道,因爲在執行時對象的實際類型可能不同。

記住多態性允許引用派生類型作爲其基本類型的實例 - 只知道和關心的編譯器是參考聲明的類型 - 在這種情況下,參考是指潛在的有效投故允許。

0

Foo2是Foo1。 Foo1不是Foo2,但這是演員試圖斷言的內容。現實生活中的比喻是:孩子通常從父母那裏繼承錢財,但父母並不從孩子那裏繼承錢財。

我覺得你的痛苦在這裏。當我第一次開始OOP時,我遇到了很多這樣的問題,並且花了我一些時間來圍繞一些細微的差別。如果我有一個Foo1並且我希望它獲得附加功能,爲什麼我不能在另一個方向施放並獲得這些額外的屬性和方法?不幸的是,這不是它的工作原理。隨着時間和練習,它變得更容易。

相關問題