重新精讀《Java 編程思想》系列之向上轉型與向下轉型


前言

今天重讀了一下向上轉型與向下轉型,有些新的體會,了解了向上轉型的好處,及如何向下轉型。在此分享給大家。

向上轉型

向上轉型是用來表現新類和基類之間的關系。在傳統中,由導出類轉型成基類,在繼承圖中是向上移動的。因此稱作向上轉型。由於向上轉型是從一個較專用類型向較通用類型轉換,所以總是安全的。也就是說,導出類是基類的一個超集。它可能比基類含有更多的方法。但他必須具備基類中所含有的方法。
我們來看一個例子。

class Car {
    public void run() {
        System.out.println("這是父類run()方法");
    }
}

public class Benz extends Car {
    public void run() {
        System.out.println("這是Benz的run()方法");

    }

    public void price() {
        System.out.println("Benz:800000$");
    }

    public static void main(String[] args) {
        Car car = new Benz();
        car.run();
       //car.price();程序報錯
    }
}

運行后輸出。這是Benz的run()方法。
但是當我們用car這個對象去調用Benz類中price這個方法時,就會報錯。
這就是因為我們此處進行的向上轉型,car這個對象雖然指向子類,但是子類由於進行了向上轉型,就失去了使用父類中所沒有的方法的“權利”,在此處就是不能調用price()這個方法。
那么向上轉型到底有什么用呢,到目前為止我們不僅看不到它的好處,反而發現使用了向上轉型后反而不能調用子類所特有的方法了。那么向上轉型的作用到底是什么呢,我們一起來看下面的代碼:

class Car {
    public void run() {
        System.out.println("這是父類run()方法");
    }

    public void speed() {
        System.out.println("speed:0");
    }

}

class BMW extends Car {
    public void run() {
        System.out.println("這是BMW的run()方法");
    }

    public void speed() {
        System.out.println("speed:80");
    }
}

public class Benz extends Car {
    public void run() {
        System.out.println("這是Benz的run()方法");

    }

    public void speed() {
        System.out.println("speed:100");
    }

    public void price() {
        System.out.println("Benz:800000$");
    }

    public static void main(String[] args) {
        show(new Benz());//向上轉型實現
        show(new BMW());
    }

    public static void show(Car car) {//父類實例作為參數
        car.run();
        car.speed();
    }
}

上面代碼中

    public static void main(String[] args) {
        show(new Benz());
        show(new BMW());
    }

    public static void show(Car car) {
        car.run();
        car.speed();
    }

就體現了向上轉型的優點,這也體現了Java抽象編程的思想。如果此處沒有向上轉型,要實現show每個子類的功能,那么有幾個子類就要寫多少函數。代碼如下:

    public static void main(String[] args) {
        show(new Benz());
        show(new BMW());
    }

    public static void show(Benz benz) {
        benz.run();
        benz.speed();
    }
    public static void show(BMW bmw) {
        bmw.run();
        bmw.speed();
    }

試想一下,一旦有很多子類,那么這個工作量將會比沒有使用向上轉型大很多。這也表明向上轉型還有個優點就是提高了代碼的簡潔性。
我們再來一種帶着static的特殊調用情況。

public class Animal {
	
	    String name = "我是動物";
	    static int age = 20;
	    public void eat() {
	        System.out.println("動物可以吃飯");
	    }
	    public static void sleep() {
	        System.out.println("動物可以睡覺");
	    }

	    public void run(){
	        System.out.println("動物可以奔跑");
	    }

	    public static void main(String[] args) {
	        Animal am = new Dog();
	        am.eat();
	        am.sleep();
	        am.run();
	        //am.watchdog();這里會報錯
	        System.out.println(am.name);
	        System.out.println(am.age);
	    }

}
class Dog extends Animal {
    String name = "小狗";
    static int age = 60;
    public void eat() {
        System.out.println("小狗可以吃飯");
    }
    public static void sleep() {
        System.out.println("小狗可以睡覺");
    }
    public void watchdog() {
        System.out.println("小狗可以看門");
    }

}

運行結果:

但是可以看到代碼塊中,直接調用Dog的watchdog()方法會報錯。

這就是因為我們此處進行的向上轉型,am這個對象雖然指向子類,但是子類由於進行了向上轉型,就失去了使用父類中所沒有的方法的“權利”,在此處就是不能調用watchdog()這個方法。
而且結果里也可以看到,睡覺是引用的父類“Animal”的睡覺方法,這是因為Animal的睡覺方法為靜態方法,可以總結如下:
如果是訪問成員變量,編譯的話就是看父類,運行同樣是看父類。
如果訪問的方法,編譯就看父類,運行則看子類。
如果是靜態方法,編譯和運行都是看父類。

向下轉型

先看一個錯誤的例子

public class Animal {
    public void eat(){
        System.out.println("Animal eat()");
    }
}

public class Dog extends Animal {
    @Override
    public void eat(){
        System.out.println("Dog eat");
    }
}

public class Test {
    public static void main(String[] args) {
        //向下轉型
        Animal animal = new Animal();
        ((Dog)animal).eat();
    }
}

運行結果:
Exception in thread "main" java.lang.ClassCastException: com.hello.test.Animal cannot be cast to com.hello.test.Dog
at com.hello.test.Test.main(Test.java:7)
從上述例子來看,Java似乎並不支持向下轉型,真是如此嗎?其實不然,Java同樣支持向下轉型,只是向下轉型是有條件的——只有引用子類對象的父類引用才能被向下轉型為子類對象。也就是說,向下轉型之前,必須先向上轉型。

public class Animal {
    public void eat(){
        System.out.println("Animal eat()");
    }
}

public class Dog extends Animal {
    @Override
    public void eat(){
        System.out.println("Dog eat");
    }
}

public class Test {
    public static void main(String[] args) {
        //向上轉型
        Animal animal = new Dog();
        //向下轉型
        ((Dog)animal).eat();
    }
}

運行結果:
Dog eat


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM