正確理解java中參考型別剪裁(Casting)

來源:互聯網
上載者:User
Java一個很迷人的特性就是支援運行時多態(Polymophism). 這個特性省卻了許多維護類型的煩惱。你可以使用一個父類的引用(reference)指向一個子類,然後在運行時調用子類的方法。這樣無論以後你擴充了多少子類,子類的子類,都可以不更改任何代碼使得程式繼續運行,JVM在運行時會照顧一切。 為了說明這一點,請看下面例子:

example 1:

public class Animal {
    public void move() {
        System.out.println("Animal: generic move");
    }
}

public class Horse extends Animal {
    public void move() {
        System.out.println("Horse: run");
    }
    public void jump() {
        System.out.println("Horse: jump");
    }   
}

public class TestAnimals {
    public static void move(Animal a) {
        a.move();
    }
   
    public static void main(String[] args) {
        Animal[] animals = {new Animal(), new Horse(), new Animal()};
        for (Animal a:animals) {
            move(a);
        }
    }
}

當我們運行UseAnimal的時候,結果如下:
Animal: generic move
Horse: run

如上所述,這樣做的一個好處是,我們只需要在UseAnimals中定義一個move(Animal a)方法,就可以對所有Animal進行操作, 無須理會具體的類型。如果我們animals數組中增加一個Dog類型,我們一樣可以調用 move方法,只要Dog類重寫了Animal中的move方法。

讓我們回到類型轉換的話題上來。在UseAnimals的main方法中. 在Java中,類及其子類的關心可以形象的看成一棵樹,其中父類在上,子類在下。向上裁剪顧名思義就是把一個子類裁剪為父類,這樣做的結果就是子類將“丟失”部分成員,而只能使用和父類共同擁有的成員,更準確的說是共有的API.

比如一下代碼是不能通過編譯的:

Animal animal = new Horse(); //upcasting
animal.jump(); //compiler complains, it cannot see "jump()"

在Java中,向上裁剪總是允許的,不需要強制指定轉換類型。 道理顯而易見,把一個較寬的類型轉換為一個交窄的類型總是可以的! 與upcasting 相對應的是downcasting。在上述例子中,Horse定義了一個jump方法。如果我們確實知道我們處理的是一個Horse對象,想調用它的jump方法,該怎樣做?

請看一下代碼:

public class TestAnimals {
    public static void move(Animal a) {
        a.move();
    }
   
    public static void main(String[] args) {
        Animal[] animals = {new Animal(), new Horse(), new Animal()};
        for (Animal a:animals) {
            move(a);
            if (a instanceof Horse) {
                Horse h = (Horse)a;
                h.jump();
            }
        }
    }
}

為了實現這個目的,我們必須顯示的把一個Animal的引用轉化為一個Horse的引用,而且我們必須清楚的知道,我們要轉化的對象確實是一個Horse對象,這就是為什麼要使用instanceof的原因了。如果沒有instanceof這個判斷,會怎麼樣?

請看修改過的代碼:
public class TestAnimals {
    public static void move(Animal a) {
        a.move();
    }
   
    public static void main(String[] args) {
        Animal[] animals = {new Animal(), new Horse(), new Animal()};
        for (Animal a:animals) {
            move(a);
            Horse h = (Horse)a;
            h.jump();
        }
    }
}
這段代碼可以(居然可以!!)通過編譯,可是啟動並執行時候就會出錯:

Animal: generic move
Exception in thread "main" java.lang.ClassCastException: com.my.Animal cannot be cast to com.my.Horse
    at com.my.TestAnimals.main(TestAnimals.java:12)

oop!你也許會抱怨為什麼編譯器不能發現這個錯誤,因為很明顯, 怎能把一個Horse對象轉換成一個Animal對象呢? 難道是把對象放在數組中編譯器發現不了? 事實上編譯器允許你這樣做:

Animal a = new Animal()
Horse h = (Horse)a;

這是因為編譯器只能確定兩個對象是否在一棵類型樹中,而無法檢測它們具體的關係。假如你嘗試這樣做:

Animal a= new Animal()
Stirngs s = (String)a;

編譯器會提示你無法把一個Animal類型轉換為String類型。 但是當待轉換類型和轉換後的類型是在同一棵類型樹的時候,情況就不同了,因為它有可能是正確的,也有可能是錯誤的,一切只有等到運行時才能發現。再來看以下代碼:
(假設Horse有一個FlyableHorse的子類)

Animal a = new FlyableHorse()
Horse h = (Horse)a;

這兩行代碼可以通過編譯,而且運行時也不會出錯。因為a 指向的是Horse的子類FlyableHorse, 當然可以把一個FlyableHorse對象裁剪為一個Horse對象。 記住, 一切都在啟動並執行時候才知道!!!

所以我們可以總結如下:

在進行向上裁剪時,一切都是自然而然的,安全的,你不需要做任何東西
在進行向下裁剪時,你必須顯示的進行類型轉換,而且你知道你現在做的事情是正確的。 編譯器不會幫你糾正錯誤,一切只有啟動並執行時候才知道。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.