(一)java
先給出個例子,代碼以下:編程
/** * @author WangQun * 動物抽象類 */ abstract class Animal { public abstract void speak(); public void eat(){ // 悶頭吃,不作額外的事情 } } /** * @author WangQun * 門神接口 */ interface DoorGod { void guard(); } /** * @author WangQun * 貓,繼承自動物 */ class Cat extends Animal { @Override public void eat() { try { Thread.sleep( 1000 ); } catch (InterruptedException e) { e.printStackTrace(); } super .eat(); } @Override public void speak() { System.out.println( " 喵喵 " ); } } /** * @author WangQun * 狗,繼承自動物,實現門神接口 */ class Dog extends Animal implements DoorGod{ @Override public void speak() { System.out.println( " 汪汪 " ); } public void guard() { while ( true ){ System.out.println( " 汪汪 " ); } } }
其中Animal爲基類,定義speak和eat方法,eat方法給出了空實現; DoorGod爲門神接口,定義了 guard方法來守護家門; Cat爲繼承Animal的子類,這裏假定貓有挑食的習慣,在eat中要耽擱點時間看看伙食;Dog也爲繼承Animal的子類,同時它實現了DoorGod接口來守護家門。 安全
先說說上溯造型(upcasting)。這個術語緣於繼承關係圖的傳統畫法:將基類至於頂部,而向下發展的就是派生類。根據上面的sample,我給出下面的一個小應用:框架
public class Main { /** * @param animal * 上溯,傳入的參數強制轉換成其父類 */ public static void upcasting(Animal animal){ animal.speak(); animal.eat(); } public static void main(String[] args) { Animal dog1 = new Dog(); upcasting(dog1); Dog dog2 = new Dog(); upcasting(dog2); } }
因爲upcasting(Animal animal)方法的參數是 Animal類型的,所以若是傳入的參數是 Animal的子類,傳入的參數就會被轉換成父類Animal類型,這樣你建立的Dog對象能使用的方法只是Animal中的簽名方法;也就是說,在上溯的過程當中,Dog的接口變窄了,它自己的一些方法(例如實現了 DoorGod的guard方法)就不可見了。若是你想使用Dog中存在而Animal中不存在的方法(好比guard方法),編譯時不能經過的。因而可知,上溯造型是安全的類型轉換。另外一方面,雖然upcasting(Animal animal)方法的參數是 Animal類型,但傳入的參數能夠是Animal的派生類(這也是OO編程中慣用的編程方法),這裏面就有個對象的類型識別問題,也就是運行時類型識別(run-time type identification,縮寫爲RTTI) ,這也能夠單獨寫一篇文章了,《Thinking in Java》中的第10章詳細地闡述了RTTI。ide
相對於類型轉換安全的上溯造型,下溯造型就未必是安全的了。咱們常常會作些強制類型轉換的事情,有時咱們也會無心間遇到 ClassCastException的轉換異常(從這一點來講,咱們應該多用範型來避免不安全的類型轉換)。例如:測試
public static void downcasting(Animal animal){ //判斷類是否是實現自哪一個接口 if(animal instanceof DoorGod){ DoorGod doorGod = (DoorGod)animal; doorGod.guard(); } if(animal instanceof Cat){ Cat cat = (Cat)animal; cat.speak(); } }
若是沒有采起措施(上面使用的措施是instanceof)判斷對象的類型,那麼向下的強制轉換就是不安全的。這種轉換錯誤在編譯時是不能檢測出來的,只有在運行時纔會拋出 ClassCastException異常,對於測試來講,這樣的錯誤也是很難檢測的。spa
總的來講,上溯造型與下溯造型概念上仍是很簡單的。但即使最簡單的東西,咱們也有可能犯下錯誤。用了那麼多的框架後,回頭看看基礎知識,發現本身的根基並非想象中的牢固阿。code
注:本文參考了《Thinking in Java》,這本書真須要細細品味啊!對象
(二)繼承
當你拿到一個對象的引用時(例如參數),你可能須要判斷這個引用真正指向的類。因此你須要從該類繼承樹的最底層開始,使用instanceof操做符判斷,第一個結果爲true的類即爲引用真正指向的類。
例以下面的例子:
class Person{} class Student extends Person{} class Postgraduate extends Student{} class Animal{} public class InstanceofTester { public static void main(String[] args) { instanceofTest(new Student()); } public static void instanceofTest(Person p){ // 判斷p的真正類型 if (p instanceof Postgraduate){ System.out.println("p是類Postgraduate的實例"); } else if(p instanceof Student){ System.out.println("p是類Student的實例"); } else if(p instanceof Person){ System.out.println("p是類Person的實例"); } else if(p instanceof Object) { System.out.println("p是類Object的實例"); } /*if(p instanceof Animal){//此錯編譯錯誤,因此作註釋 System.out.println("p是類Animal的實例"); }*/ } }
這個程序的輸出結果是:p是類Student的實例Person類所在的繼承樹是:Object<--Person<--Student<--Postgraduate。這個例子中還加入一個Animal類,它不是在Person類的繼承樹中,因此不能做爲instanceof的右操做數。你能夠跑跑程序,應該就明白什麼意思了。