在上一个问题(linked here)中,有一些关于对象继承的未解决问题仍然不清楚。 (我对术语的使用可能不正确 - 请相应编辑 - 谢谢)
简单地说,为什么超类不会继承已明确赋给它的子类的属性。代码示例:
UIView *view = nil;
UILabel *label = [[UILabel alloc] init];
label.text = @"some text...";
view = label;
// view has not inherited any UILabel properties
view.text = @"new text..."; //error
view.textColor = [UIColor redColor]; //error
我想了解一下这个问题。
谢谢。
答案 0 :(得分:3)
这是面向对象的概念101.
您写道:“简单地说,为什么超类不会继承已明确分配给它的子类的属性”。
因为它恰恰相反。子类继承其超类的属性。
子类是超类的专用版本。超类可以有几个子类。子类知道其特殊功能以及其超类的公共功能。但是一个超类知道从它创建的任何子类的NOTHING。怎么可能呢?子类可以存在于一个完全独立的库中,该库在几年后由完全不同的人编写。
在您的特定示例代码中,您有UILabel
,这是一个专门的UIView
。 UIView
对UILabel
的任何专业功能一无所知。
当你尝试:
view.text = @"some text";
编译器抱怨,因为它查看UIView
的定义,但找不到名为text
的属性。所以你得到一个编译错误。
由于您碰巧在运行时知道view
实际上会指向UILabel
,您可以使用强制转换来欺骗编译器:
((UILabel *)view).text = @"some text";
或使用setter语法编写它:
[(UILabel *)view setText:@"some text"];
通过使用演员你告诉编译器 - “相信我,我知道它看起来像UIView
变量,但我真的知道它是UILabel
变量”。
这是关于使用演员表的重要说明。你在欺骗编译器。你可能错了。如果您错了,那么您的应用很可能会在运行时崩溃。
答案 1 :(得分:2)
类继承就像专门化一样。
想想班级人物。
一个人知道如何呼吸,走路,说话,喂养自己等等(所以,让我们说所有人都有MakeMeASandwich
方法。)
一个人可能有一份专门的工作:木匠,厨师,水管工,IT人员,卡车司机等。
如果您有变量
Person *aPerson;
它是指向Person的指针。你不知道这个人做了什么交易,你只知道它是一个人。您可以将Plumber对象分配给该变量,Carpenter对象或任何其他专用Person,因为所有这些类型也属于通用类Person。
当你提到aPerson时,编译器不知道它与谁交谈的是什么类型的人。
编译器可能知道它可以告诉Person制作一个三明治,并假设所有人都知道如何做到这一点。 (所有专门的Person类,如Carpenter和Plumber都从其父类Person继承makeMeASandwich方法。)
除非你告诉编译器:
"此人是木匠"然后编译器会抱怨如果你要求人为你建立一个套牌,因为你的普通人没有buildADeck方法。只有Carpenter型人才有这种方法。
当你说
时[(Carpenter *)aPerson buildADeck]
你告诉编译器"这个人是木匠。相信我。我知道。让他/她建造一个甲板。"编译器假设你知道你在说什么,那个人真的是一个木匠。它会要求该人建立一个套牌。
如果在运行时,事实证明aPerson变量指向的Person对象不是Carpenter类型,程序将崩溃,因为当你告诉糕点厨师建造一个套牌时,他/她会很困惑不知道该怎么办。他/她甚至可能会生气并退出。
如果你说:
Person *aPerson = [[Carpenter alloc] init];
您告诉编译器创建一个Carpenter,并将其存储在一个通用的" Person"变量。编译器立即忘记它创建的人是Carpenter。它只知道它是一个人。
因此,如果您尝试说
,编译器会抱怨[aPerson buildADeck];
你必须说
[(Carpenter *)aPerson buildADeck]
但是,既然所有人都知道如何制作三明治,你可以说
[aPerson makeMeASandwich]
并让它工作。即使这个人是水管工,他/她也会知道如何做三明治,并且(希望)会让你成为一个三明治。如果这个人是厨师,它可能是一个更好的三明治,但至少你会得到一个三明治。
你也可以说
Plumber * aPlumber = [[Plumber alloc] init]; [aPlumber makeMeASandwich];
因为管道工也是人,并且知道如何制作三明治。 (根据OOP术语,管道工从其父类Person继承makeMeASandwich方法。)
答案 2 :(得分:1)
我不了解Objctive-C,但至于一般性问题:
以这种方式思考 - 超类(或者确实 - 编译器或链接器)的用户如何知道它具有这些属性?它被超类类型引用,因此对于所有意图和目的 - 它是超类(尽管是多态,因为它是运行时机制)。
答案 3 :(得分:1)
这不是关于继承,而是关于你告诉编译器的内容,在这种情况下,view
属于UIView *
类型。你可以为这个变量分配你想要的任何东西(只要它是一个子类,或者你让编译器开心)。
因此,编译器认为它是UIView *
,即使它实际上指向UILabel
的实例,因此编译器只对UIView
方法和属性感到满意。
如果你强制转换,那么编译器会很高兴,并且在运行时,如果你对编译器撒谎,事情就会变得混乱。