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 关键字来故意阻止其他程序员直接访问数据并强制他们在编写他们的类时只能使用我们的公共方法。

抽象类也是如此。抽象类的作者不希望创建该类的对象。相反,作者希望从抽象类继承抽象方法,然后重写。

这种方法的优势在大型项目中显而易见。你拥有的类越多,你就越需要清楚地描述它们的角色。您将在不久的将来看到这种方法的好处。每件事都要经过这个。