Why are Metaclasses instances of Metaclass and not Class?
I'd like to know the reasoning开发者_Go百科 why there are two different classes used, instead of just using Class for both.
The short answer is "Your assumption that Classes are instances of a system wide Class is wrong, each Class is actually an instance of a Class specific Metaclass" and also "it wouldn't work any other way".
A slight modification to the answer is "the names are really confusing, it's sometimes easier to just memorize their roles and not try to think too much about how they work".
Short hairy answer:
Metaclass is an instance, just like all other ordinary Smalltalk Classes, and it requires it's own dedicated class. Each instance of a regular Smalltalk object has a class, with inheritance following the class hierarchy. Each class is itself an instance of a class specific metaclass, with inheritance following the metaclass hierarchy. Each metaclass is itself an instance of the Metaclass class, which is short-circuited by the virtual machine in a neat little trick, since no one has found a use to giving the Metaclass class a parent, and all those who tried have generally found that their sanity was starting to erode in the process.
Longer, still hairy answer:
Smalltalk allows for each class to contain class specific messages. These are equivalent, roughly, to static methods in Java - but with some significant differences. One of these differences is that Smalltalk classes are actually instantiated objects - they are live objects in the system, complete with the ability to inherit from other objects, and to contain instance variables.
This property leads to a potential multitude of inheritance hierarchies in the system. Regular objects each are instances of exactly one Class, with message sends to an objects searching the objects' class, and then following up the inheritance chain of the Class hierarchy. Messages sent to regular objects are resolved up the Class hierarchy.
Also, class objects are each instances of one class specific Metaclass. Messages sent to a class object are resolved by lookups to the class specific metaclass, and then upwards on the metaclass hierarchy.
Up one more layer, metaclass objects each are instances of a system wide unique Metaclass class. Messages sent to a metaclass object are looked up in the system-wide unique Metaclass, which is not allowed to inherit from anybody and is hard-wired as a short circuit in the VM.
Technically, the system has two inheritance hierarchies, and a third one that is faked with a short circuit. There's no theoretical reason to stop at two, though there are lots of practical ones. This allows each object to have it's own, object unique, messages, each class to have it's own, class unique, messages, and forces all Metaclasses to answer to just one set of messages, defined in the Metaclass.
Neat, huh?
You clearly need two ClassDescriptions for a class: one for the instance methods, and one for the class methods.
Now, you might still try to use Class for both objects. I think one reason for having Metaclass is that you need to distinguish the two. If you look at the methods theMetaClass
and theNonMetaClass
, you see that they are mirroring each other: you go from metaclass to class throught thisclass member, and from class to metaclass through the regular class pointer (that any object posseses). If they both were of instance Class, they couldn't know which side they implement - unless there was a flag slot, which is worse than creating subclasses.
This is one of those things that I understand perfectly... until I have to explain it ;-) After much distillation...
Given:
- an object's behavior is specified by its class
- classes are objects, too
- regular-objects/classes have behavior that only makes sense for regular-objects/classes
Therefore, classes and instances should have different behavior-specifiers (i.e. classes).
Looked at another way, what we call "instance side" represents the instance side of the class. What we call "class side" represents the instance side of the metaclass.
For example, Class defines #addClassVarNamed: and #addInstVarNamed:, because those both make sense on the instance side, where Metaclass only defines #addInstVarNamed:, because there are no class side class variables.
To dig deeper into the minutia, see "Pharo By Example" or "Fundamentals of Smalltalk Programming Technique"
精彩评论