Java物件型別轉換和強制物件型別轉換

2020-07-16 10:05:15
Java 語言允許某個型別的參照變數參照子類的範例,而且可以對這個參照變數進行型別轉換。如果把參照型別轉換為子類型別,則稱為向下轉型;如果把參照型別轉換為父類別型別,則稱為向上轉型。

例如,Creature 類表示生物類,Animal 類表示動物類,該類對應的子類有 Dog 類,使用物件型別表示如下:
Animal animal = new Dog();
Dogdog = (Dog) animal; // 向下轉型,把Animal型別轉換為Dog型別
Creature creature = animal; // 向上轉型,把Animal型別轉換為Creature型別

例 1

下面通過具體的範例演示物件型別的轉換。例如,父類別 Animal 和子類 Cat 中都定義了範例變數 name、靜態變數 staticName、實體方法 eat() 和靜態方法 staticEat()。此外,子類 Cat 中還定義了範例變數 str 和實體方法 dogMethod()。

父類別 Animal 的程式碼如下:
public class Animal {
    public String name = "Animal:動物";
    public static String staticName = "Animal:可愛的動物";

    public void eat() {
        System.out.println("Animal:吃飯");
    }

    public static void staticEat() {
        System.out.println("Animal:動物在吃飯");
    }
}
子類 Cat 的程式碼如下: 
public class Cat extends Animal {
    public String name = "Cat:貓";
    public String str = "Cat:可愛的小貓";
    public static String staticName = "Dog:我是喵星人";

    public void eat() {
        System.out.println("Cat:吃飯");
    }

    public static void staticEat() {
        System.out.println("Cat:貓在吃飯");
    }

    public void eatMethod() {
        System.out.println("Cat:貓喜歡吃魚");
    }

    public static void main(String[] args) {
        Animal animal = new Cat();
        Cat cat = (Cat) animal; // 向下轉型
        System.out.println(animal.name); // 輸出Animal類的name變數
        System.out.println(animal.staticName); // 輸出Animal類的staticName變數
        animal.eat(); // 輸出Cat類的eat()方法
        animal.staticEat(); // 輸出Animal類的staticEat()方法
        System.out.println(cat.str); // 呼叫Cat類的str變數
        cat.eatMethod(); // 呼叫Cat類的eatMethod()方法
    }
}
通過參照型別變數來存取所參照物件的屬性和方法時,Java 虛擬機器將採用以下系結規則:
  • 實體方法與參照變數實際參照的物件的方法進行系結,這種系結屬於動態系結,因為是在執行時由 Java 虛擬機器動態決定的。例如,animal.eat() 是將 eat() 方法與 Cat 類系結。
  • 靜態方法與參照變數所宣告的型別的方法系結,這種系結屬於靜態系結,因為是在編譯階段已經做了系結。例如,animal.staticEat() 是將 staticEat() 方法與 Animal 類進行系結。
  • 成員變數(包括靜態變數和範例變數)與參照變數所宣告的型別的成員變數系結,這種系結屬於靜態系結,因為在編譯階段已經做了系結。例如,animal.name 和 animal.staticName 都是與 Animal 類進行系結。

對於 Cat 類,執行時將會輸出如下結果:
Animal:動物
Animal:可愛的動物
Cat:吃飯
Animal:動物在吃飯
Cat:可愛的小貓
Cat:貓喜歡吃魚

強制物件型別轉換

Java 編譯器允許在具有直接或間接繼承關係的類之間進行型別轉換。對於向下轉型,必須進行強制型別轉換;對於向上轉型,不必使用強制型別轉換。

例如,對於一個參照型別的變數,Java 編譯器按照它宣告的型別來處理。如果使用 animal 呼叫 str 和 eatMethod() 方法將會出錯,如下:
animal.str = "";    // 編譯出錯,提示Animal類中沒有str屬性
animal.eatMethod();    // 編譯出錯,提示Animal類中沒有eatMethod()方法

如果要存取 Cat 類的成員,必須通過強制型別轉換,如下:
((Cat)animal).str = "";    // 編譯成功
((Cat)animal).eatMethod();    // 編譯成功

把 Animal 物件型別強制轉換為 Cat 物件型別,這時上面兩句編譯成功。對於如下語句,由於使用了強制型別轉換,所以也會編譯成功,例如:
Cat cat = (Cat)animal;    // 編譯成功,將Animal物件型別強制轉換為Cat物件型別

子類的物件可以轉換成父類別型別,而父類別的物件實際上無法轉換為子類型別。因為通俗地講,父類別擁有的成員子類肯定也有,而子類擁有的成員,父類別不一定有。因此,對於向上轉型,不必使用強制型別轉換。例如:
Cat cat = new Cat();
Animal animal = cat;    // 向上轉型,不必使用強制型別轉換

如果兩種型別之間沒有繼承關係,那麼將不允許進行型別轉換。例如:
Dog dog = new Dog();
Cat cat = (Cat)dog;    // 編譯出錯,不允許把Dog物件型別轉換為Cat物件型別