Java 面向對象-抽象類

由於多態的存在,每個子類都可以覆寫父類的方法,例如:

class Person {
    public void run() { … }
}

class Student extends Person {
    @Override
    public void run() { … }
}

class Teacher extends Person {
    @Override
    public void run() { … }
}

Person類派生的StudentTeacher都可以覆寫run()方法。

如果父類Personrun()方法沒有實際意義,能否去掉方法的執行語句?

class Person {
    public void run(); // Compile Error!
}

答案是不行,會導致編譯錯誤,因爲定義方法的時候,必須實現方法的語句。

能不能去掉父類的run()方法?

答案還是不行,因爲去掉父類的run()方法,就失去了多態的特性。例如,runTwice()就無法編譯:

public void runTwice(Person p) {
    p.run(); // Person沒有run()方法,會導致編譯錯誤
    p.run();
}

如果父類的方法本身不需要實現任何功能,僅僅是爲了定義方法簽名,目的是讓子類去覆寫它,那麼,可以把父類的方法聲明爲抽象方法:

class Person {
    public abstract void run();
}

把一個方法聲明爲abstract,表示它是一個抽象方法,本身沒有實現任何方法語句。因爲這個抽象方法本身是無法執行的,所以,Person類也無法被實例化。編譯器會告訴我們,無法編譯Person類,因爲它包含抽象方法。

必須把Person類本身也聲明爲abstract,才能正確編譯它:

abstract class Person {
    public abstract void run();
}

抽象類

如果一個class定義了方法,但沒有具體執行代碼,這個方法就是抽象方法,抽象方法用abstract修飾。

因爲無法執行抽象方法,因此這個類也必須申明爲抽象類(abstract class)。

使用abstract修飾的類就是抽象類。我們無法實例化一個抽象類:

Person p = new Person(); // 編譯錯誤

無法實例化的抽象類有什麼用?

因爲抽象類本身被設計成只能用於被繼承,因此,抽象類可以強迫子類實現其定義的抽象方法,否則編譯會報錯。因此,抽象方法實際上相當於定義了“規範”。

例如,Person類定義了抽象方法run(),那麼,在實現子類Student的時候,就必須覆寫run()方法:

public class Main {
    public static void main(String[] args) {
        Person p = new Student();
        p.run();
    }
}

abstract class Person {
    public abstract void run();
}

class Student extends Person {
    @Override
    public void run() {
        System.out.println("Student.run");
    }
}

面向抽象編程

當我們定義了抽象類Person,以及具體的StudentTeacher子類的時候,我們可以通過抽象類Person類型去引用具體的子類的實例:

Person s = new Student();
Person t = new Teacher();

這種引用抽象類的好處在於,我們對其進行方法調用,並不關心Person類型變量的具體子類型:

// 不關心Person變量的具體子類型:
s.run();
t.run();

同樣的代碼,如果引用的是一個新的子類,我們仍然不關心具體類型:

// 同樣不關心新的子類是如何實現run()方法的:
Person e = new Employee();
e.run();

這種儘量引用高層類型,避免引用實際子類型的方式,稱之爲面向抽象編程。

面向抽象編程的本質就是:

  • 上層代碼只定義規範(例如:abstract class Person);

  • 不需要子類就可以實現業務邏輯(正常編譯);

  • 具體的業務邏輯由不同的子類實現,調用者並不關心。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章