|
|
記得繼承了什麼? 中,SwordsMan與Magician都繼承了Sprite,要建立SwordsMan與Magician,並宣告變數來參考它們,可以這麼寫: SwordsMan swordsMan = new SwordsMan();
Magician magician = new Magician(); 這是最基本的,而在 千面人 中,你知道,運用「是一種」的關係,你還可以這麼寫: Sprite sprite1 = new SwordsMan();
Sprite sprite2 = new Magician(); 這沒有問題,因為SwordsMan是一種Sprite,而Magician是一種Sprite,程式的讀法可從右往左,判別方式就是「右邊的東西是不是一種左邊的東西」。 你可以將自己當作編譯器,檢查程式邏輯,例如: Sprite sprite1 = new SwordsMan();
沒問題,這會編譯成功,因為從右往左讀,SwordsMan是一種Sprite。但如果: SwordsMan swordsMan = new Sprite();
就會編譯失敗,如果你是編譯器,你會抱怨,Sprite不一定是種SwordsMan,所以就不想編譯。下面這個也是: Sprite sprite1 = new SwordsMan();
SwordsMan swordsMan = sprite1; 第一行編譯沒問題,第二行呢?Java是靜態語言,變數宣告時會帶有型態,就編譯器的角度來說,在看到第二行時,它發現sprite1是Sprite型態,那麼由右往左看,Sprite是一種SwordsMan嗎?不一定!所以就不想編譯通過。除非你這麼寫: Sprite sprite1 = new SwordsMan();
SwordsMan swordsMan = (SwordsMan) sprite1; 一般會說,第二行作了轉型(Cast),但白話的說法是,編譯器原本 很囉嗦,告訴你Sprite不一定是種SwordsMan,但你要編譯器住嘴,所以在前面加上了(SwordsMain),這相當於告訴編譯器:「我知道 啦!Sprite不一定是種SwordsMan,不過我已經用(SwordsMan)告訴你,它就是SwordsMan,你就別再囉嗦了!」,編譯器於是:「好吧!那我就編譯囉!後果你自己負責!」 就上例而言,執行時也不會有問題,但是如果是下面這個: Sprite sprite1 = new Magician();
SwordsMan swordsMan = (SwordsMan) sprite1; 這會編譯成功,在第二行時,編譯器其實會發現,Sprite不一定是SwordsMan,但也發現,你加了(SwordsMan),所以這表示你自己後果 自負,所以就通過編譯,因此沒有編譯時期錯誤訊息。但事實上,sprite1明明就參考至Magician實例,你硬要將魔法師轉職為 SwordsMan,後果就是ClassCastException,這是執行時期錯誤。 所謂的後果自負,就是執行時期出搥,這可不是編譯器的錯…XD 在Java中,轉型的概念基本上就是如此,只是要編譯器住嘴。 你可以多作一些練習: SwordsMan swordsMan = new SwordsMan();
Sprite sprite = swordsMan; 這會編譯成功,記得,對判斷是不是「是一種」關係,編譯器每次只看一行: SwordsMan swordsMan = new SwordsMan();
Sprite sprite = swordsMan; SwordsMan swordsMan = sprite; 上面第三行會編譯失敗,因為就第三行來看,sprite是Sprite型態的變數,Sprite不一定是種SwordsMan,所以編譯失敗是必然的。除非你叫編譯器別囉嗦: SwordsMan swordsMan = new SwordsMan();
Sprite sprite = swordsMan; SwordsMan swordsMan = (SwordsMan) sprite; 上例編譯成功,執行也不會有錯誤,因為sprite實際上還是參考至SwordsMan實例。但下面這個就會執行時期錯誤: SwordsMan swordsMan = new SwordsMan();
Sprite sprite = swordsMan; Magician magician = (Magician) sprite; 編譯可以成功,但實際上,sprite是參考至SwordsMan,執行時間你要它轉型為Magician,就會丟出ClassCastException。 要不要轉型,就看你想不想讓編譯器住嘴,一旦要它住嘴,那麼你就要自己確定執行時間沒有錯誤。 所以在 Promotion 與 Cast 中,是否要轉型,也是可以如此判斷。例如: float PI = 3.14;
編譯器會抱怨,3.14是double型態,怎麼可以裝入float呢?如果你明確要它住嘴,那麼就可以這麼寫: float PI = (float) 3.14;
後果呢?就是如果精度被裁掉了,那編譯器可不管了! |