在下面例子中,若已經知道某個“可能是”Truck的Object混在Vehicle陣列v內,要如何做才能讓這個Truck object執行Truck的專屬method(carryCargo())?
class Vehicle { void run() { System.out.println("Vehicle run"); } } class Truck extends Vehicle { void run() { System.out.println("Truck run"); } void carryCargo() { System.out.println("carryCargo"); } } class CastTest { public static void main(String[] args) { Vehicle[] v = { new Vehicle(), new Truck(), new Vehicle() }; for (Vehicle vehicle: v) { vehicle.run(); if (vehicle instanceof Truck) { vehicle.carryCargo(); //卡車專屬的method } } } }
如上,會有Compiler error - cannot find symbol。原因是編譯器認為,Vehicle Class並沒有carryCargo()可以使用。這時候方法其實就是Casting來轉型。
if (vehicle instanceof Truck) { Truck t = (Truck) vehicle;//Casting這個reference variable,變成Truck t.carryCargo(); }
另外上面的cast程式碼也可以寫成 ((Truck)vehicle).carryCargo();
此時,編譯器必須看到所有的括號,否則它會認為這段程式碼是不完整的。
從superClass的型態轉成比較narrow的superClass型態稱為downcast。在使用downcast的時候可以使用instanceof來測試,如果成功來才做轉型,避免失敗。
有時Compiler會選擇相信程式碼,比如下面的範例:
Vehicle v1 = new Vehicle(); Truck t = (Truck) v1;
Compiler並不會報錯,但在runtime的時候會有Exception跳(java.lang.ClassCastException),說明無法轉型。原因是Compiler會檢查Truck和Vehicle是否在同一個inheritance tree上,而且在轉型之前的程式碼其實也是有可能成功運行的。Compiler會讓可能運行成功的程式碼通過。
當然,如果非常明顯cast會有問題的程式碼,Compiler還是會擋住(比如他們不是在同一個inheritance tree上)
以下示範upcast(向上轉型):
Truck t = new Truck(); Vehicle v1 = t; // 隱晦的轉型 Vehicle v2 = (Vehicle) t; // 明確的轉型
上面兩種寫法都compile以及runtime都會成功,且結果也一樣。其理由為,Truck IS-A Vehicle,這也表示Vehicle能做的事情,Truck一定可以做。Compiler和JVM都知道,所以就算沒有明確的cast也是可以的。
試想情境:
假設Baby extends Human,且Human extends Animal (Baby->Human->Animal),則其實Baby也是extends Animal的,因為Baby IS-A Human,繼承關係也代表Baby IS-A Animal。
若有個Mammals Interface被Human implements(Human --> Mammals)且實作了,一旦Baby extends Human,這表示Baby也有Mammals的method可以使用了。就算在Baby再一次宣告 implements Mammals,也是可以的(為了讓其他人可以從API知道Baby IS-A Mammals,而不需要去看Human API),且Baby不需要再實作一次Mammals內的method也可以,因為Human已經幫忙實作了。當然Baby可以重新定義從Human那繼承來的Mammals method。
結論:
一個concrete class如果implements interface,但卻沒有implements interface內的abstract class,並不代表有問題,必需往上看他所有的superclass是否已經曾經實作過了。假如superclass的其中一個節點已經實作過,那麼下面的子子孫孫不需要再實作一次了。
沒有留言:
張貼留言