En la programación orientada a objetos , una metaclase es una clase cuyas instancias son clases en sí mismas. A diferencia de las clases ordinarias, que definen los comportamientos de los objetos, las metaclases especifican los comportamientos de las clases y sus instancias. No todos los lenguajes de programación orientados a objetos admiten el concepto de metaclases. Para aquellos que lo hacen, el grado de control que tienen las metaclases sobre los comportamientos de las clases varía. Las metaclases a menudo se implementan tratando a las clases como ciudadanos de primera clase , lo que convierte a una metaclase en un objeto que crea y administra estas clases. Cada lenguaje de programación se adhiere a su propio protocolo de metaobjetos , que son las reglas que determinan las interacciones entre objetos, clases y metaclases. [1] Las metaclases se utilizan para automatizar la generación de código y mejorar el desarrollo del marco. [2]
En Python , la clase incorporada type
es una metaclase. [3] [4] [5] Considere esta simple clase de Python:
clase Auto : def __init __ ( self , marca : str , modelo : str , año : int , color : str ) : self.marca = make self.modelo = modelo self.año = año self.color = color @property def description ( self ) -> str : """Devuelve una descripción de este coche.""" return f " { self . color } { self . make } { self . model } "
En tiempo de ejecución, Car
es una instancia de type
. El código fuente de la Car
clase, que se muestra arriba, no incluye detalles como el tamaño en bytes de Car
los objetos, su distribución binaria en memoria, cómo se asignan, que el __init__
método se llama automáticamente cada vez Car
que se crea a, etc. Estos detalles entran en juego no solo cuando Car
se crea un nuevo objeto, sino también cada vez que Car
se accede a cualquier atributo de a. En lenguajes sin metaclases, estos detalles están definidos por la especificación del lenguaje y no se pueden anular. En Python, la metaclase -- type
controla estos detalles del Car
comportamiento de . Se pueden anular utilizando una metaclase diferente en lugar de type
.
El ejemplo anterior contiene código redundante relacionado con los cuatro atributos make
, model
, year
y color
. Es posible eliminar parte de esta redundancia utilizando una metaclase personalizada. En Python, una metaclase se define más fácilmente como una subclase de type
.
clase AttributeInitType ( tipo ): def __call__ ( self , * args , ** kwargs ): """Crear una nueva instancia.""" # Primero, crea el objeto de la forma predeterminada normal. obj = type . __call__ ( self , * args ) # Además, establezca atributos en el nuevo objeto. para nombre , valor en kwargs . items (): setattr ( obj , nombre , valor ) # Devuelve el nuevo objeto. return obj
Esta metaclase solo anula la creación de objetos. Todos los demás aspectos del comportamiento de la clase y del objeto siguen siendo manejados por type
.
Ahora se puede reescribir la clase Car
para utilizar esta metaclase. En Python 3, esto se hace proporcionando un "argumento de palabra clave" metaclass
a la definición de clase:
clase Car ( object , metaclass = AttributeInitType ): @property def description ( self ) -> str : """Devuelve una descripción de este coche.""" return " " .join ( str ( value ) for value in self .__ dict __ . values ())
El objeto resultante Car
se puede instanciar como de costumbre, pero puede contener cualquier número de argumentos de palabras clave:
new_car = Coche ( marca = 'Toyota' , modelo = 'Prius' , año = 2005 , color = 'Verde' , motor = 'Híbrido' )
En Smalltalk , todo es un objeto . Además, Smalltalk es un sistema basado en clases , lo que significa que cada objeto tiene una clase que define la estructura de ese objeto (es decir, las variables de instancia que tiene el objeto) y los mensajes que entiende un objeto. En conjunto, esto implica que una clase en Smalltalk es un objeto y que, por lo tanto, una clase debe ser una instancia de una clase (llamada metaclase).
Como ejemplo, un objeto de automóvil c
es una instancia de la clase Car
. A su vez, la clase Car
es nuevamente un objeto y, como tal, una instancia de la metaclase de Car
llamada Car class
. Observe el espacio en blanco en el nombre de la metaclase. El nombre de la metaclase es la expresión de Smalltalk que, cuando se evalúa, da como resultado el objeto de metaclase. Por lo tanto, la evaluación Car class
da como resultado el objeto de metaclase para Car
cuyo nombre es Car class
(se puede confirmar esto evaluando Car class name
que devuelve el nombre de la metaclase de Car
).
Los métodos de clase pertenecen en realidad a la metaclase, al igual que los métodos de instancia pertenecen en realidad a la clase. Cuando se envía un mensaje al objeto 2
, la búsqueda del método comienza en Integer
. Si no se encuentra, continúa hacia arriba en la cadena de la superclase y se detiene en Object, independientemente de si se encuentra o no.
Cuando se envía un mensaje a Integer
la búsqueda del método comienza en Integer class
y continúa hasta la cadena de superclase Object class
. Observe que, hasta ahora, la cadena de herencia de metaclase sigue exactamente la cadena de herencia de clase. Pero la cadena de metaclase se extiende más allá porque Object class
es la subclase de Class
. Todas las metaclases son subclases de Class.
En los primeros Smalltalks, solo había una metaclase llamada Class
. Esto implicaba que los métodos que tenían todas las clases eran los mismos, en particular el método para crear nuevos objetos, es decir, new
. Para permitir que las clases tuvieran sus propios métodos y sus propias variables de instancia (llamadas variables de instancia de clase y que no deben confundirse con las variables de clase ), Smalltalk-80 introdujo para cada clase C
su propia metaclase C class
. Esto significa que cada metaclase es efectivamente una clase singleton .
Dado que no existe ningún requisito de que las metaclases se comporten de manera diferente entre sí, todas las metaclases son instancias de una única clase llamada Metaclass
. Metaclass
Se llama a la metaclase de Metaclass class
que, a su vez, es una instancia de la clase Metaclass
.
En Smalltalk-80, cada clase (excepto Object
) tiene una superclase . La superclase abstracta de todas las metaclases es Class
, que describe la naturaleza general de las clases.
La jerarquía de superclases para las metaclases es paralela a la de las clases, excepto la clase Object
. TODAS las metaclases son subclases de Class
, por lo tanto:
Object class superclass == Class.
Al igual que los gemelos unidos , las clases y las metaclases nacen juntas. Metaclass
tiene una variable de instancia thisClass
que apunta a su clase unida. Tenga en cuenta que el explorador de clases habitual de Smalltalk no muestra las metaclases como clases separadas. En cambio, el explorador de clases permite editar la clase junto con su metaclase al mismo tiempo.
Los nombres de las clases en la jerarquía de metaclases se confunden fácilmente con los conceptos del mismo nombre. Por ejemplo:
Object
es la clase base que proporciona métodos comunes para todos los objetos; "un objeto" es un entero, un widget, un Car
, etc.Class
es la base de las metaclases que proporciona métodos comunes para todas las clases (aunque no es una metaclase en sí misma); "una clase" es algo como Integer
, o Widget
, o Car
, etc.Metaclass
Proporciona métodos comunes para todas las metaclases.Cuatro clases proporcionan las funciones necesarias para describir nuevas clases. Su jerarquía de herencia (de Object) y las principales funciones que proporcionan son:
Ruby purifica el concepto de metaclases de Smalltalk-80 introduciendo clases propias, eliminando la Metaclass
clase y redefiniendo (o des)el mapa de clases. El cambio se puede esquematizar de la siguiente manera: [6]
Obsérvese en particular la correspondencia entre las metaclases implícitas de Smalltalk y las clases propias de Ruby. El modelo de clases propias de Ruby hace que el concepto de metaclases implícitas sea completamente uniforme: cada objeto x tiene su propio metaobjeto, llamado la clase propia de x , que es un metanivel superior a x . Las clases propias de "orden superior" suelen existir de forma puramente conceptual: no contienen ningún método ni almacenan ningún otro dato en la mayoría de los programas Ruby. [7]
Los siguientes diagramas muestran una muestra de la estructura central de Smalltalk-80 y Ruby en comparación. [8]
En ambos lenguajes, la estructura consta de una parte incorporada que contiene los objetos circulares (es decir, objetos que aparecen en un ciclo formado por una combinación de enlaces azules o verdes) y una parte de usuario que tiene cuatro objetos explícitos: clases A
y B
y objetos terminales u
y v
. Los enlaces verdes muestran la relación de herencia hijo→padre (con la dirección ascendente implícita), los enlaces azules muestran la relación de instanciación de miembro complementario→contenedor (un enlace azul desde x apunta al contenedor menos real de x que es el punto de inicio para la búsqueda de método cuando se invoca un método en x ). Los nodos grises muestran las clases propias (resp. metaclases implícitas en el caso de Smalltalk-80).
El diagrama de la derecha también ofrece una imagen de la evaluación diferida de clases propias en Ruby. El v
objeto puede tener su clase propia evaluada (asignada) como consecuencia de agregar métodos singleton a v
.
Según el método de introspección de Ruby llamado class
, la clase de cada clase (y de cada eigenclass) es constantemente la Class
clase (denotada por c
en el diagrama). Class
, y Struct
son las únicas clases que tienen clases como instancias. [9] [ disputado – discutir ] La subclasificación de Class
no está permitida. Siguiendo la definición estándar de metaclases podemos concluir que Class
y Struct
son las únicas metaclases en Ruby. Esto parece contradecir la correspondencia entre Ruby y Smalltalk, ya que en Smalltalk-80, cada clase tiene su propia metaclase. La discrepancia se basa en el desacuerdo entre el class
método de introspección en Ruby y Smalltalk. Mientras que el mapa x ↦ x.class
coincide en objetos terminales, difiere en la restricción a clases. Como ya se mencionó anteriormente, para una clase x
, la expresión de Ruby x.class
evalúa constantemente a Class
. En Smalltalk-80, si x
es una clase entonces la expresión x class
corresponde a la de Ruby x.singleton_class
– que evalúa a la eigenclass de x
.
Las metaclases en Objective-C son casi las mismas que en Smalltalk-80, lo cual no sorprende ya que Objective-C toma mucho de Smalltalk. Al igual que Smalltalk, en Objective-C las variables de instancia y los métodos están definidos por la clase de un objeto. Una clase es un objeto, por lo tanto es una instancia de una metaclase.
Al igual que Smalltalk, en Objective-C, los métodos de clase son simplemente métodos llamados en el objeto de clase, por lo tanto, los métodos de clase de una clase deben definirse como métodos de instancia en su metaclase. Debido a que las diferentes clases pueden tener diferentes conjuntos de métodos de clase, cada clase debe tener su propia metaclase independiente. Las clases y las metaclases siempre se crean como un par: el entorno de ejecución tiene funciones objc_allocateClassPair()
y objc_registerClassPair()
para crear y registrar pares de clase-metaclase, respectivamente.
No hay nombres para las metaclases; sin embargo, se puede hacer referencia a un puntero a cualquier objeto de clase con el tipo genérico Class
(similar al tipo id
que se utiliza para un puntero a cualquier objeto).
Debido a que los métodos de clase se heredan a través de herencia, como Smalltalk, las metaclases deben seguir un esquema de herencia paralelo al de las clases (por ejemplo, si la clase padre de la clase A es la clase B, entonces la clase padre de la metaclase de A es la metaclase de B), excepto el de la clase raíz.
A diferencia de Smalltalk, la metaclase de la clase raíz hereda de la propia clase raíz (que normalmente NSObject
utiliza el marco Cocoa ). Esto garantiza que todos los objetos de la clase sean, en última instancia, instancias de la clase raíz, de modo que se puedan utilizar los métodos de instancia de la clase raíz (normalmente métodos de utilidad útiles para objetos) en los propios objetos de la clase.
Dado que los objetos de metaclase no se comportan de forma diferente (no se pueden agregar métodos de clase para una metaclase, por lo que todos los objetos de metaclase tienen los mismos métodos), todos son instancias de la misma clase: la metaclase de la clase raíz (a diferencia de Smalltalk). Por lo tanto, la metaclase de la clase raíz es una instancia de sí misma. La razón de esto es que todas las metaclases heredan de la clase raíz; por lo tanto, deben heredar los métodos de clase de la clase raíz. [10]
Los siguientes son algunos de los lenguajes de programación más destacados que admiten metaclases.
Algunos lenguajes menos difundidos que admiten metaclases son OpenJava , OpenC++, OpenAda, CorbaScript , ObjVLisp, Object-Z , MODEL-K, XOTcl y MELDC. Varios de estos lenguajes datan de principios de los años 1990 y son de interés académico. [12]
Logtalk , una extensión orientada a objetos de Prolog , también admite metaclases.
Tanto el Marco de descripción de recursos (RDF) como el Lenguaje de modelado unificado (UML) admiten metaclases.