Understanding Object Inheritance

2019-09-04 23:29发布

问题:

In a previous question (linked here), there were some unresolved issues about object inheritance which remain unclear. (my use of terminologies may be incorrect - please edit accordingly - thank you)

Put simply, why does the superclass not inherit the properties of a subclass that has been expressly assigned to it. A code example:

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

Just would like to get my head around this.

Thank you.

回答1:

Class inheritance is like specialization.

Think of the class Person.

A Person knows how to breath, walk, talk, feed himself, etc. (So let's say all people have a MakeMeASandwich method.)

A person might have a specialized job: carpenter, cook, plumber, IT person, truck driver, etc.

If you have a variable

Person *aPerson;

It is a pointer to a Person. You don't know what trade that Person does, you just know it's a Person. You can assign a Plumber object to that variable, or a Carpenter object, or any other specialized Person, because all of those types also belong to the general class Person.

When you refer to aPerson, the compiler has no idea what type of person it's talking to.

The compiler might know that it can tell the Person to make a sandwich, and assume all people know how to do that. (All the specialized Person classes like Carpenter and Plumber inherit the makeMeASandwich method from their parent Person class.)

Unless you tell the compiler:

"This Person is a Carpenter" then the compiler is going to complain if you ask the Person to build you a deck, because your average Person doesn't have a buildADeck method. Only Carpenter-type people have that method.

When you say

[(Carpenter *)aPerson buildADeck]

You are telling the compiler "This Person is a Carpenter. Trust me. I know. Ask him/her to build a deck." The compiler assumes you know what you're talking about, and that that Person really is a Carpenter. It will ask the Person to build a deck.

If, at runtime, it turns out the Person object pointed to by the aPerson variable is not of type Carpenter, the program will crash because when you tell the pastry chef to build a deck, he/she will be very confused and not know what to do. He/she might even get mad and quit.

If you say:

Person *aPerson = [[Carpenter alloc] init];

You are telling the compiler to create a Carpenter, and store it in a general-purpose "Person" variable. The compiler promptly forgets that the person it created was a Carpenter. It just knows that it's a Person.

So, the compiler will the complain if you try to say

[aPerson buildADeck];

You have to say

[(Carpenter *)aPerson buildADeck]

But, since all people know how to make a sandwich, you can say

[aPerson makeMeASandwich] 

and have it work. Even if the Person is a Plumber, he/she will know how to make a sandwich, and will (hopefully) make you one. It will probably be a better sandwich if the Person is a cook, but at least you'll get a sandwich.

you can also say

Plumber *aPlumber = [[Plumber alloc] init]; [aPlumber makeMeASandwich];

because Plumbers are people too, and know how to make sandwiches. (IN OOP terms, Plumbers inherit the makeMeASandwich method from their parent Person class.)



回答2:

This is object oriented concepts 101.

You wrote: "Put simply, why does the superclass not inherit the properties of a subclass that has been expressly assigned to it".

Because it's the opposite. A subclass inherits the properties of its superclass.

A subclass is a specialized version of the superclass. The superclass can have several subclasses. A subclass knows about its specialized features as well as the public features of its superclass. But a superclass knows NOTHING about any subclasses created from it. How could it? A subclass could exist in a completely separate library written years later by a completely different person.

In your specific sample code, you have a UILabel which is a specialized UIView. UIView knows absolutely nothing about any of the specialized features of UILabel.

When you try to do:

view.text = @"some text";

the compiler complains because it looks at the definition for UIView and doesn't find a property named text. So you get a compilation error.

Since you happen to know at runtime that view will in reality be pointed to a UILabel, you could trick the compiler using a cast:

((UILabel *)view).text = @"some text";

or write it using the setter syntax:

[(UILabel *)view setText:@"some text"];

By using the cast you are telling the compiler - "trust me, I know it looks like a UIView variable but I really know it's a UILabel variable".

Here's an important note about using a cast. You are tricking the compiler. You could be wrong. If you are wrong then your app will most likely crash at runtime.



回答3:

I have no knowledge of Objctive-C, but as for the general question:

Think about it this way - how would the user of the superclass (or indeed - the compiler or linker) know that it has these properties? It is being referenced by superclass type, thus for all intents and purposes - it is the superclass (polymorphism notwithstanding, as it is a runtime mechanism).



回答4:

This isn't so much about inheritance as it is about what you've told the compiler, and in this case it's that view is of type UIView *. You can assign anything you want to that variable (so long as it is a subclass or you make the compiler happy).

So, the compiler thinks it's a UIView * even though it is really pointing to an instance of UILabel, so the compiler is only happy with UIView methods and properties.

If you cast then the compiler will be happy and at runtime things will get messy if you lied to the compiler.