Java 的上溯造型和下溯造型以及舉例,以及判斷參數等指向的類

(一)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爲基類,定義speakeat方法,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的接口變窄了,它自己的一些方法(例如實現了 DoorGodguard方法)就不可見了。若是你想使用Dog中存在而Animal中不存在的方法(好比guard方法),編譯時不能經過的。因而可知,上溯造型是安全的類型轉換。另外一方面,雖然upcasting(Animal animal)方法的參數是 Animal類型,但傳入的參數能夠是Animal的派生類(這也是OO編程中慣用的編程方法),這裏面就有個對象的類型識別問題,也就是運行時類型識別(run-time type identification,縮寫爲RTTI) ,這也能夠單獨寫一篇文章了,《Thinking in Java》中的第10章詳細地闡述了RTTIide

        相對於類型轉換安全的上溯造型,下溯造型就未必是安全的了。咱們常常會作些強制類型轉換的事情,有時咱們也會無心間遇到 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的右操做數。你能夠跑跑程序,應該就明白什麼意思了。

相關文章
相關標籤/搜索