1.公共基類

今天,我們將享受各種有趣的話題。還記得我們引入ChessItem基類來簡化所有代表棋子的類嗎?我希望如此🙂

現在想像一下,每一塊都有一個draw()方法來處理在屏幕上繪製它。您調用該draw()方法,棋子就會在其當前坐標處自行繪製。將此方法移至基類會很方便。

如果該draw()方法在 ChessItem 基類中,那麼我們可以在棋子類中重寫它並編寫如下優雅的代碼:

class ChessBoard
{
   public void drawAllChessItems()
   {
      // Add the pieces to the list
      ArrayList<ChessItem> items = new ArrayList<ChessItem>();
      items.add(new King());
      items.add(new Queen());
      items.add(new Bishop());

      // Draw them regardless of their type
      for(ChessItem item: items)
      {
         item.draw();
      }
   }
}

通過引入ChessItem基類,我們能夠大大簡化代碼:不需要分別調用每個類的方法,我們可以輕鬆地將所有對象存儲在一個集合中,等等。

但是這裡有一個有趣的問題:draw()直接在ChessItem類中聲明的方法應該在屏幕上繪製什麼?畢竟棋中沒有這顆棋子,也就沒什麼可畫的了。

這是完全正確的。更何況,ChessItem直接創建對像是沒有意義的。它不是棋子,而只是一個抽象——我們為方便起見而創建的一個類。這就是OOP中抽象的工作方式:我們將重要的數據和方法(所有部分共享的)移至基類,並將它們的差異保留在單獨的後代類中。


2. 抽像類

抽像類

對於這種情況,Java 有一種特殊的類:抽像類。它們旨在使程序員更容易使用類似的類並減少其中重複代碼的數量。

以下是有關抽像類的三件事。

沒有實現的方法

抽像類可以有一個沒有實現的方法聲明。這正是使方法抽象的原因。方法體只是用分號代替。在方法名稱之前,我們寫上關鍵字abstract。例子:

public abstract class ChessItem
{
   public int x, y; // Coordinates
   private int value; // The piece's value
   public int getValue() // Ordinary method that returns value field
   {
      return value;
   }

   public abstract void draw(); // Abstract method. The implementation is missing.
}

抽像類

每個沒有實現的方法都標有 abstract 關鍵字。如果一個類甚至只有一個抽象方法,該類也用關鍵字標記abstract

禁止創建對象

您不能創建抽像類的對象。這樣的代碼根本無法編譯。

代碼 描述
ChessItem item = new ChessItem();
item.draw();
此代碼無法編譯
ChessItem item = new Queen();
item.draw();
你可以這樣做

繼承一個抽像類

如果你的類繼承了一個抽像類,那麼你需要覆蓋所有繼承的抽象方法,即你需要為它們編寫一個實現。否則,您的類本身也必須聲明為抽象的。

如果一個類甚至有一個直接在其中聲明或從父類繼承的未實現方法,則該類被認為是抽象的。

為什麼這一切都是必要的?為什麼需要抽像類?不是可以用普通的代替嗎?而不是抽象方法,我們不能只寫兩個空的大括號作為方法體嗎?

我們可以。但是這些限制類似於修飾符private。我們使用 private 關鍵字來故意阻止其他程序員直接訪問數據並強制他們在編寫他們的類時只能使用我們的公共方法。

抽像類也是如此。抽像類的作者不希望創建該類的對象。相反,作者希望從抽像類繼承抽象方法,然後重寫。

這種方法的優勢在大型項目中顯而易見。你擁有的類越多,你就越需要清楚地描述它們的角色。您將在不久的將來看到這種方法的好處。每件事都要經過這個。