元类

在面向对象程序设计中，元类（）是一种实例是类的类。普通的类定义的是特定对象的行为，元类定义的则是特定的类及其实例的行为。不是所有面向对象编程语言都支持元类。在它能做的事情之中，元类可以覆写任何给定方面类行为的程度是不同的。元类可以通过使类成为头等对象来实现，在这种情况下元类简单的就是构造类的一个对象。每个语言都有它自己的元对象协议，给出对象、类和元类如何交互的规则。

Smalltalk-80元类
在Smalltalk中，所有东西都是对象。此外，Smalltalk是基于类的系统，这意味着所有对象都有一个类，它定义这个对象的结构（比如说这个类拥有实例变量），和这个对象所理解的消息。二者在一起蕴含了，在Smalltalk中，类是一个对象，因此类也需要是它的元类的实例。

元类在Smalltalk-80系统中的主要角色，是提供协议来初始化类变量，和建立元类的唯一实例（也就是其对应的类）的初始化实例。

实例联系
为了允许类拥有它们自己的方法，和叫作类实例变量它们自己的，Smalltalk-80为每个类C介入了它们自己的元类C class。就像实例方法实际上属于类一样，类方法实际上属于元类。在类中定义和，而在元类中定义类实例变量。

每个元类在效果上都是单例类。就像连体双胞胎，类和元类是共生的。元类有一个实例变量thisClass，它指向它结合的类。平常的Smalltalk，不将元类展示为单独的类，转而允许一起同时编辑类和它的元类。

要得到一个实例的类，需要向它发送消息调用class方法。类和元类继承了其超类的name方法，它返回接收者名字的字符串。例如，轿车对象c是类Car的实例，则c class返回Car类对象，而c class name返回'Car'；依次类推，Car class返回Car的元类对象，而Car class name返回依赖于实现，有的是nil，即没有名字，有的是'Car class'，即用空格分隔的类名字和'class'。

在早期的Smalltalk-76中，创建新类的方式是向Class类发送new消息。在Smalltalk-80中，Class是元类的基础类，它是类而不是元类。所有元类都是一个Metaclass类的实例。Metaclass类是Metaclass class的实例，而Metaclass class作为元类，也是Metaclass类的实例。

继承联系
在Smalltalk-80中，终端对象是一个整数、一个组件、或一台车等，而类是像Integer、或Widget或Car等这样的东西，除了Object之外，所有的类都有一个超类。元类所继承的元类，就是元类对应的类所继承的类的元类。

在一个消息被发送到对象的时候，方法的查找开始于它的类。如果没有找到则在上行超类链，停止于Object而不管找到与否。在一个消息被发送到一个类的时候，类方法查找开始于它的元类，并上行超类链至Object class。直至Object class，元类的超类层级并行于类的超类层级。在Smalltalk-80中，Object class是Class的子类：
Object class superclass == Class.
类方法的查找在元类链之后仍可继续下去，所有元类都是Class的在继承层级中的子类，它是所有元类的抽象超类，它描述这些类的一般性质，继而最终可上溯至Object。

继承层级
四个类提供描述新类的设施，下面是它们的继承层级（起自Object），和它们提供的主要设施：
Object，对象类是所有类的基础类，它为所有对象提供公共的方法，即公共的缺省行为。至少包括了：测试对象的功能比如class方法，比较对象，对象复制，访问对象的各部份，打印和存储对象，错误处理。
Behavior，行为类定义了拥有实例的对象所需要的最小，它提供建立一个类的实例的new方法。特别是，它定义了Smalltalk-80解释器所用到的状态，并为编译方法源代码提供到编译器的基本接口，如compile:等方法。Behavior描述的这个状态，包括了一个类层级连接（superclass:），一个方法字典（methodDictionary:、addSelector:withMethod:），和对实例的描述（依据数目和对它们的变量的表示）。尽管一个类的多数设施都规定在Behavior中，但很多消息不能于此实现，对类的完全描述转而在它的子类之中提供。
ClassDescription，类描述类为Class和Metactass提供了共同的超类。它表现类命名（name）、类注释（comment:）、和命名实例变量（addlnstVarName:）。特别是，它增加了组织在方法字典中方法（compile:classified:）和类自身（category:）的结构。它还提供了在外部串流（文件）上存储完全的类描述的机制，和记述对类描述的变更的机制。
Class，类类是所有元类的基础类，从而为所有类提供公共的方法，它定义了初始化类变量的initialize方法。Class的实例描述对象的表现和行为，它提供比ClassDescription更具描述性的设施，特别是，它增加了对类变量名字（addClassVarName:）和共享的池变量（addSharedPool:）的表示。它还提供比Behavior更综合性的编程支持设施，比如创建一个类的子类的消息：subclass:instanceVariableNames:classVariableNames:poolDictionaries:category:。
Metaclass，元类类是创建元类的类，它为所有元类提供公共的方法。Metaclass的关键性的消息，是自身初始化消息，这在GNU Smalltalk中依旧保留；一个是发送到Metaclass自身的消息subclassOf: superMeta，用来创建元类superMeta的一个子类；一个是发送到Metaclass的一个实例的消息，用来建立这个元类的唯一实例，对于建立完全初始化的类，它的每个参数都是需要的：name:environment:subclassOf:instanceVariableNames:shape:classVariableNames:poolDictionaries:category:。

方法查找次序
下面是方法查找次序的辨析：
每个终端对象，在查找方法时，都首先查找自己的类；然后按类继承链上溯，最后不经过Class（类类）和Metaclass（元类类），最终上至Object（对象类）。
每个类，包括Class和Metaclass，在查找查找方法时，首先查找自己的元类；然后按元类继承链上溯，最终经过Object class（对象元类）而上至Class；接着按类继承链上溯，不经过与其并列的Metaclass，最终上至Object。
每个元类，包括Class class和Metaclass class，在查找方法时，因为都是Metaclass的实例，所以首先查找Metaclass；然后按类继承链上溯，不经过与其并列的Class，最终上至Object。

示意图
下面是两个示意图，二者都是纵向连线表示实例联系，而横向连线表示继承联系。实例联系以Metaclass（元类类）及其元类为顶端，而继承联系以Object（对象类）及其元类为中心，其中Object class（对象元类）继承Class（类类）是串接元类继承链与类继承链的关键环节。前者图示采用Smalltalk-80蓝皮书的样式（但旋转了180°），将Metaclass及其元类放置在最上方的独立两行，使得实例联系尽量成为树状向上汇聚；后者图示将Metaclass及其元类放置在最左边，使得继承联系尽量都在同一行之上。

例子
下列例子展示，从Smalltalk-80派生的Squeak和Pharo的样例代码的结构，它们的继承层级的根类实际上是ProtoObject，ProtoObject封装了所有对象都必须拥有的极小化的消息集合，它被设计为引发尽可能多的错误，用来支持代理（proxy）定义。例如Smalltalk-80的Object中，错误处理消息doesNotUnderstand:，和系统原始消息become:，就转而在ProtoObject中定义了。

在示意图中，纵向的绿色连接，展示继承联系的“子→父”关系（隐含的自下而上），横向的蓝色连接展示实例联系的“成员→容器”关系，从x出的发蓝色连接，指向x的最小实际容器，它是在调用在x上的方法时查找方法的继承链起点：

这个结构由两个部份构成，用户部份有四个显式的对象和类及其两个隐式的元类：终端对象u和v，它们连接到的类A和B，它们两个连接到的右侧灰色节点表示的隐式的元类，其他的对象都是内建部份。

Objective-C元类
在Objective-C中的元类，几乎同于Smalltalk-80的元类，这是因为Objective-C从Smalltalk引进了很多东西。就像Smalltalk，在Objective-C中实例变量和方法是对象的类定义的。类也是对象，因此它是元类的一个实例。

就像Smalltalk，在Objective-C中类方法，简单的是在类对象上调用的方法，因此一个类的类方法，必须定义为在它的元类中的实例方法。因为不同的类有不同的类方法集合，每个类都必须有它自己单独的元类。类和元类总是成对创建：运行时系统拥有函数objc_allocateClassPair()和objc_registerClassPair()来分别的创建和注册类-元类对。

元类没有名字，但是到任何类对象的指针，可以通过泛化类型Class来提及（类似于用作到任何对象的指针的类型id）。

元类都是相同的类即根类元类的实例，而根类元类是自身的实例。因为类方法是通过继承联系来继承的，就像Smalltalk，除了根类元类之外，元类继承联系必须并行于类继承联系（比如说如果类A的父类是类B，则A的元类的父类是B的元类）。

不同于Smalltalk，根类元类继承自根类自身（通常为使用Cocoa框架的NSObject）。这确保了所有的元类最终都是根类的子类，从而人们可以将根类的实例方法，它们通常是针对对象有用的实用方法，使用于类对象自身上。

Python元类
在Python中，内建的类type是元类。

类的定义，不包括它的实例对象的细节，如它们的字节为单位的大小，它们在内存中的二进制格局，它们是如何分配的，每次建立实例时自动调用的__init__方法，诸如此类。不只是在建立新实例对象的时候，而且在每次访问实例对象的任何特性的时候，这些细节都起到作用。在没有元类的语言中，这些细节是在语言规定中定义的，并且不能被覆写（override）。

在Python中，元类type控制着类行为的这些细节，默认定义出的类自身都是type的实例。新的元类可以很容易定义为type的子类从而覆写它，通过向类定义提供“关键字参数”metaclass就可以使用这个新的元类。

>>> type(b)
<class '__main__.B'>
>>> print(type(B), B.__bases__, [*B.__dict__])
<class '__main__.M'> (<class '__main__.A'>,) ['__module__', '__doc__']
>>> print(type(A), A.__bases__, [*A.__dict__])
<class '__main__.M'> (<class 'object'>,) ['__module__', '__dict__', '__weakref__', '__doc__']
>>> print(type(M), M.__bases__, [*M.__dict__])
<class 'type'> (<class 'type'>,) ['__module__', '__doc__']
>>> print(type(c), c.__bases__)
<class 'type'> (<class 'object'>,)
>>> print(type(r), r.__bases__)
<class 'type'> ()
>>> sorted({*r.__dict__} & {*c.__dict__})
['__delattr__', '__dir__', '__doc__', '__getattribute__', '__init__', '__new__', '__repr__', '__setattr__', '__sizeof__']
>>> sorted({*r.__dict__} - {*c.__dict__})
['__class__', '__eq__', '__format__', '__ge__', '__gt__', '__hash__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__reduce__', '__reduce_ex__', '__str__', '__subclasshook__']
>>> sorted({*c.__dict__} - {*r.__dict__})
['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__', '__dict__', '__dictoffset__', '__flags__', '__instancecheck__', '__itemsize__', '__module__', '__mro__', '__name__', '__prepare__', '__qualname__', '__subclasscheck__', '__subclasses__', '__text_signature__', '__weakrefoffset__', 'mro']

例子
考虑下面这个最简单的Python类：

class Car:
    def __init__(self, *args, **kwargs):
        self.__dict__.update(kwargs)
    def __call__(self, **kwargs):
        self.__dict__.update(kwargs)
    @property
    def description(self):
        """返回这辆车的描述."""
        return " ".join(str(value) for value in self.__dict__.values())
>>> new_car = Car(make='Toyota', model='Prius', year=2005, engine='Hybrid')
>>> new_car(color='Green')
>>> new_car.description
'Toyota Prius 2005 Hybrid Green'

上面的例子包含了一些代码来处理初始化特性，也可以使用元类来完成这种任务：
class AttributeInitType(type):
    def __new__(*args, **kwargs):
        """返回创建的实例类."""
        cls = type.__new__(*args, **kwargs)
        def call(self, **kwargs):
            self.__dict__.update(kwargs)
        cls.__call__ = call    # 为实例类增加__call__方法
        return cls
    def __call__(cls, *args, **kwargs):
        """返回为实例类创建的实例对象."""
        obj = type.__call__(cls, *args)    # 以正常缺省方式建立实例对象。
        obj.__dict__.update(**kwargs)    # 在这个新对象上设置属性。
        return obj

这个元类只覆写实例类和对象创建部份功能。元类行为的所有其他方面仍由type处理。现在可以重写类Car使用这个新元类：
class Car(object, metaclass=AttributeInitType):
    def __init__(self, *args): pass # 接收未预期的位置实际参数
    @property
    def description(self):
        """返回这辆车的描述."""
        return " ".join(str(value) for value in self.__dict__.values())

Ruby元类
Ruby通过介入其自称的特征类（eigenclass），提炼了Smalltalk-80的元类概念，去除了Metaclass类，并重新定义了class-of映射。变更可以图示如下：

特别要注意在Smalltalk的隐含的元类和Ruby类的特征类之间的对应。Ruby的特征类模型，使得隐式元类概念完全统一：所有对象x，都有它自己的元对象，它叫作x的特征类，它比x高一个元层级。高阶特征类通常是纯粹概念上的存在，在大多数Ruby程序中，它们不包含任何方法也不存储任何（其他）数据。

下面的示意图展示Ruby样例代码的核心结构。这里的灰色节点表示打开A和v特征类后扩张出来的特征类。

图示还展示了Ruby中特征类的惰性求值。v对象可以有它的特征类，作为向v增加“单例方法”的结果而被求值（被分配）。

在语言和工具中的支持
下面是支持元类的一些最显著的编程语言。

 Common Lisp，通过CLOS
 Delphi和受它影响的其他Object Pascal版本
 Groovy
 Objective-C
 Python
 Perl，通过元类pragma，还有
 Ruby
 Smalltalk
 C++（规划用于C++23）

一些不甚广泛传播的语言支持元类，包括、OpenC++、OpenAda、、ObjVLisp、、MODEL-K、和MELDC。其中几种语言可追溯日期至1990年代早期并具有学术价值。

是Prolog的面向对象扩展，它也支持元类。

资源描述框架（RDF）和统一建模语言（UML）二者都支持元类。

另见 
 元编程
 元对象
 反射式编程

引用

类 (计算机科学)