类继承就像专业化。
想想 Person 类。
一个人知道如何呼吸、走路、说话、喂自己等等。(假设所有人都有一个MakeMeASandwich 方法。)
一个人可能有专门的工作:木匠、厨师、水管工、IT 人员、卡车司机等。
如果你有一个变量
Person *aPerson;
它是一个指向 Person 的指针。你不知道那个人做什么交易,你只知道它是一个人。您可以将 Plumber 对象分配给该变量、Carpenter 对象或任何其他专门的 Person,因为所有这些类型也属于通用类 Person。
当您引用 aPerson 时,编译器不知道它正在与哪种类型的人交谈。
编译器可能知道它可以告诉 Person 制作三明治,并假设所有人都知道如何做。 (所有专门的 Person 类,如 Carpenter 和 Plumber 都从其父 Person 类继承 makeMeASandwich 方法。)
除非你告诉编译器:
"This Person is a Carpenter" 那么如果你要求 Person 为你构建一个套牌,编译器就会抱怨,因为你的普通 Person 没有 buildADeck 方法。只有木匠型的人才有这种方法。
当你说
[(Carpenter *)aPerson buildADeck]
您是在告诉编译器“这个人是木匠。相信我。我知道。请他/她构建一个套牌。”编译器假设你知道你在说什么,并且那个人真的是一个木匠。它会要求 Person 构建一个套牌。
如果在运行时发现 aPerson 变量指向的 Person 对象不是 Carpenter 类型,程序就会崩溃,因为当你告诉糕点师构建一个套牌时,他/她会非常困惑并且不知道该怎么办。他/她甚至可能会生气并退出。
如果你说:
Person *aPerson = [[Carpenter alloc] init];
您告诉编译器创建一个 Carpenter,并将其存储在一个通用的“Person”变量中。编译器很快忘记了它创建的人是木匠。它只知道它是一个人。
所以,如果你尝试说,编译器会抱怨
[aPerson buildADeck];
你不得不说
[(Carpenter *)aPerson buildADeck]
但是,既然所有人都知道怎么做三明治,你可以说
[aPerson makeMeASandwich]
并让它工作。即使此人是水管工,他/她也会知道如何制作三明治,并且会(希望)为您制作三明治。如果 Person 是厨师,它可能会是一个更好的三明治,但至少你会得到一个三明治。
你也可以说
管道工 *aPlumber = [[管道工分配] init];
[aPlumber makeMeASandwich];
因为水管工也是人,他们知道如何制作三明治。 (在 OOP 术语中,管道工从其父 Person 类继承 makeMeASandwich 方法。)