“你好,阿米戈!我要和你说一个很有趣的新话题。”

“今天是有趣话题日呢!”

“谢谢你。”

“不客气。”

还记得我们之前为简化所有棋子类而引入的 ChessItem 基类吗?”

“记得。”

“现在,假如每个棋子都有一个方法可以渲染屏幕上的棋子。我们调用此方法,棋子就会在它当前的坐标上自我描画。那如果把这个方法改用到基类里,会对我们有帮助吗?”

“会。”学习了多态后,我可以对所有棋子调用渲染方法,而不受棋子类型限制。就像下面这样:”

例如:
class ChessBoard
{
  public void drawAllChessItems()
  {
  //draw them regardless of their type.
  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 对象也没有什么意义。世界上没有这个棋子。它只是一个抽象 — 我们为了方便而创造的一个类。这就是抽象在 OOP 里的运作方式:我们把所有重要的(全部棋子共享的)数据和方法移入基类,但是我们保留了与每个特定棋子相对应的类的不同之处。”

抽象类 - 1

对此,Java 有一个特别的类类型:抽象类。关于抽象类,有 3 点要牢记。

1) 抽象类可以声明方法,而无需实现它。这种方法我们称之为抽象方法。

例如:
 public abstract class ChessItem
{
 public int x, y; //coordinates
 private int value; //the piece's "value"

 public int getValue() //an ordinary method, returns value
 {
   return value;
 }

 public abstract void draw(); //abstract method. There is no implementation.

}

2) 抽象方法会带有关键字 abstract 标记

如果一个类有多个抽象方法,那这个类也可以标为 abstract

3) 我们无法给抽象类创建对象。有如此意图的代码将无法编译。

Java 语言代码 说明
ChessItem item = new ChessItem();
item.draw();
此代码将无法编译。
ChessItem item = new Queen();
item.draw();
但是你可以这样做。

4) 如果你的类继承自抽象类,就需要重写此继承类的所有方法,换言之,你要实现它们。否则,你的类也要声明为抽象类。如果类有多个未实现的方法直接在类中声明或继承自父类,那么这个类也可以认为是抽象类。

“但是为什么一定要做这些?为什么需要抽象类?不可以改用普通类吗?相较于抽象方法,我们能不能创建一个空的实现,只有左右两个括号?”

“当然可以。但是这些限制条件就像是 private 修饰符。我们使用 private 修饰符来特意拦截对数据的直接访问,如此,其他程序员和他们的类只能用我们的 public 方法。”

抽象类也是同样的道理。无论这个类是谁写的,都不希望任何人给他们的类创建实例。相反,作者会希望他或她的抽象类可以得到继承和重写。

“我还是不明白为什么我们要用这个方法来让事情变得复杂。”

“大项目里更容易说明这个功能的优点。类越多,你就越能清楚地认识到划定类的角色的重要性。不久,你会发现此举的优点。这个过程每个人都会经历。”