“你好,阿米戈!接下来,有个概念我想你会经常用到。那就是继承。

对初学者而言,编程就像是魔术。让我打个比方。

假如你是魔术师,想创造一匹飞马。你可能会想变出一匹 Pegasus。但是自然界并不存在飞马,要想变出来就很难。你会有很多工作要做。但如果先变出一匹马,然后再给它安上翅膀,事情就变得简单多了。

继承:继承的优点 - 1

在编程里,我们把这个过程叫做“继承”。假如,你要写一个非常复杂的类。首先,从头写到尾就要花很长时间。之后,你还要花大量时间去测试它,找错误。但是为什么要如此费力呢?何不先找找看你要的类是不是有已经可行的版本了?

假如,你找到了一个类,它有 80% 的功能是你想要的。你可以把它直接复制到你的类中。但是这样做会有一些缺点:

1) 你找的类可能已经被编译到字节码里了。你可能无法访问它的源代码。

2) 你可能有那个类的源代码,但如果你的代码里有几行是别人的,那么,你所在的公司可能会因此而遭到起诉,被要求赔偿数十亿的巨额罚款。而你也会因此被公司起诉。

3) 这样做还会引入很多不必要的冗余代码。如果其他类的作者发现了一个错误并更正了,但是你还是会有这个错误。

有一种更得体的方法,它不需要得到原始类作者的法律许可。在 Java 里,你可以简单地声明另一个类是你的父类。这相当于把另一个类的代码加到你的类里。父类的所有数据和方法都会出现在你的类里。例如,你可以继承 horse(即马),然后添加 wings(即翅膀),最后获得 Pegasus(即飞马)。

继承:继承的优点 - 2

“非常有趣。请继续。”

“继承还有其他用途。假如,你有十个非常相似的类。它们各自有匹配的数据和方法。你可以创建一个专门的基类,把数据(以及相关的方法)移入该基类,然后让那十个类从基类继承。换句话说,对每个类而言,你都指明它有一个父类,也就是我们所说的基类。”

“就像抽象的优点只有与封装联系起来才能得到正真体现一样,继承的优点也需要通过多态来大放异彩。具体细节我们明天再讲。今天我们先来看几个继承的例子。”

“假如,我们正在写一个国际象棋程序。我们要给棋子写类。你觉得要写哪些类呢,阿米戈?”

“King(王)、Queen(后)、Bishop(象)、Knight(马)、Rook(车)和 Pawn(兵)。”

“非常好。一个不落。”

“那你觉得这些类里要存储哪些数据呢?”

“每个棋子在棋盘上的位置(x 和 y)以及价值(worth)。毕竟,有些棋子比其他的更有价值。”

“这些类之间有什么区别?”

“这些类的区别在于棋子的走法(move),就是具体的行为。”

“对。你可以像这样来把它们定义为类:”

class King
{
int x;
int y;
int worth;
void kingMove()
{
//code that defines,
//how the king moves
}
}
class Queen
{
int x;
int y;
int worth;
void queenMove()
{
//code that defines,
//how the queen moves
}
}
class Rook
{
int x;
int y;
int worth;
void rookMove()
{
//code that defines,
//how the rook moves
}
}
class Knight
{
int x;
int y;
int worth;
void knightMove()
{
//code that defines,
//how the knight moves
}
}
class Bishop
{
int x;
int y;
int worth;
void bishopMove()
{
//code that defines,
//how the bishop moves
}
}
class Pawn
{
int x;
int y;
int worth;
void pawnMove()
{
//code that defines,
//how the pawn moves
}
}

“没错,我会这样写。”

“那不妨来看下如何通过继承来少写些代码吧。我们可以把一模一样的方法和数据放到一个公有类里。暂且命名为 ChessItem。创建 ChessItem 对象的意义不大,因为它们匹配不到任何一个棋子。但是类就可以起到很大的帮助:”

class King extends ChessItem
{
void kingMove()
{
//code that defines,
//how the king moves
}
}
class Queen extends ChessItem
{
void queenMove()
{
//code that defines,
//how the queen moves
}
}
class Rook extends ChessItem
{
void rookMove()
{
//code that defines,
//how the rook moves
}
}
 class ChessItem
{
int x;
int y;
int worth;
}
 
class Knight extends ChessItem
{
void knightMove()
{
//code that defines,
//how the knight moves
}
}
class Bishop extends ChessItem
{
void bishopMove()
{
//code that defines,
//how the bishop moves
}
}
class Pawn extends ChessItem
{
void pawnMove()
{
//code that defines,
//how the pawn moves
}
}

“太有趣了!”

“是的!对那些拥有成千上万个不同对象以及成百上千个类的大项目而言,继承的优点尤为明显。届时,选择适当的类不仅可以大大简化逻辑,还可以将所需的代码减到原来的十分之一。”

“那要怎么做才能继承一个类呢?”

“声明父类后,我们使用关键字 extends,后接该父类的名字。我们只可以继承一个类。

继承:继承的优点 - 3

这张图里有一头牛,它继承自一头猪。这只猪继承自鸡,后者则继承自蛋。每个类都只有一个父类!这个继承并不总是符合逻辑。如果你有一头猪,但是你真的想要一头牛,程序员会忍不住帮你一把,用猪变出一头牛。

“但如果我想要继承 2 个类该怎么办?有什么办法吗?”

“真没有。Java 的类不支持实现多重继承:一个类有且只有一个父类。但有类型的多重继承,即一个类可以实施多个接口。这让问题有所缓解。”

“我明白了。那什么是接口?”

“明天我们再来讲接口。现在我们继续来看继承。”