En informática , la introspección de tipos es la capacidad de un programa de examinar el tipo o las propiedades de un objeto en tiempo de ejecución . Algunos lenguajes de programación poseen esta capacidad.
La introspección no debe confundirse con la reflexión , que va un paso más allá y es la capacidad de un programa de manipular los metadatos, las propiedades y las funciones de un objeto en tiempo de ejecución. Algunos lenguajes de programación también poseen esa capacidad (por ejemplo, Java , Python , Julia y Go ).
En Objective-C , por ejemplo, tanto el objeto genérico como NSObject (en Cocoa / OpenStep ) proporcionan el método isMemberOfClass:
que devuelve verdadero si el argumento del método es una instancia de la clase especificada. El método isKindOfClass:
devuelve verdadero de manera análoga si el argumento hereda de la clase especificada.
Por ejemplo, digamos que tenemos un Apple
y una Orange
clase que hereda de Fruit
.
Ahora, en el eat
método podemos escribir
- ( void ) eat: ( id ) sth { if ([ sth isKindOfClass : [ Clase Fruit ]]) { // en realidad estamos comiendo una Fruta, así que continuamos if ([ sth isMemberOfClass : [ Clase Apple ]]) { eatApple ( sth ); } else if ([ sth isMemberOfClass : [ Clase Orange ]]) { eatOrange ( sth ); } else { error (); } } else { error (); } }
Ahora, cuando eat
se llama con un objeto genérico (an id
), la función se comportará correctamente dependiendo del tipo del objeto genérico.
C++ admite la introspección de tipos mediante las palabras claves de información de tipos en tiempo de ejecución (RTTI) typeid y dynamic_cast . La dynamic_cast
expresión se puede utilizar para determinar si un objeto en particular pertenece a una clase derivada en particular. Por ejemplo:
Persona * p = dynamic_cast < Persona *> ( obj ); si ( p != nullptr ) { p -> caminar (); }
El typeid
operador recupera un std::type_info
objeto que describe el tipo más derivado de un objeto:
si ( typeid ( Persona ) == typeid ( * obj )) { serialize_person ( obj ); }
La introspección de tipos ha sido parte de Object Pascal desde la versión original de Delphi, que utiliza RTTI en gran medida para el diseño de formularios visuales. En Object Pascal, todas las clases descienden de la clase base TObject, que implementa la funcionalidad RTTI básica. Se puede hacer referencia al nombre de cada clase en el código para fines RTTI; el identificador del nombre de clase se implementa como un puntero a los metadatos de la clase, que se pueden declarar y usar como una variable de tipo TClass. El lenguaje incluye un operador is , para determinar si un objeto es o desciende de una clase dada, un operador as , que proporciona una conversión de tipos con verificación de tipos, y varios métodos TObject. La introspección más profunda (enumeración de campos y métodos) tradicionalmente solo se admite para objetos declarados en el estado $M+ (un pragma), generalmente TPersistent, y solo para símbolos definidos en la sección publicada. Delphi 2010 aumentó esto a casi todos los símbolos.
procedimiento Form1 . MyButtonOnClick ( Sender : TObject ) ; var aButton : TButton ; SenderClass : TClass ; begin SenderClass := Sender . ClassType ; //devuelve el puntero de clase de Sender si sender es TButton entonces begin aButton := sender como TButton ; EditBox . Text := aButton . Caption ; //Propiedad que tiene el botón pero los objetos genéricos no end else begin EditBox . Text := Sender . ClassName ; //devuelve el nombre de la clase de Sender como una cadena end ; end ;
El ejemplo más simple de introspección de tipos en Java es el operador instanceof
[1] . El instanceof
operador determina si un objeto en particular pertenece a una clase en particular (o a una subclase de esa clase, o a una clase que implementa esa interfaz). Por ejemplo:
si ( obj instancia de Persona ) { Persona p = ( Persona ) obj ; p . caminar (); }
La clase java.lang.Class
[2] es la base de una introspección más avanzada.
Por ejemplo, si se desea determinar la clase real de un objeto (en lugar de si es miembro de una clase particular ), Object.getClass()
se Class.getName()
puede utilizar:
Sistema . out . println ( obj . getClass (). getName ());
En PHP, la introspección se puede realizar mediante el uso instanceof
de operadores. Por ejemplo:
if ( $obj instanceof Person ) { // Haz lo que quieras }
La introspección se puede lograr utilizando las funciones ref
y isa
en Perl .
Podemos introspeccionar las siguientes clases y sus instancias correspondientes:
paquete Animal ; sub nuevo { mi $clase = shift ; return bless {}, $clase ; } paquete Perro ; utiliza la base 'Animal' ; paquete principal ; mi $animal = Animal -> new (); mi $perro = Perro -> new ();
usando:
imprimir "Esto es un animal.\n" si ref $animal eq 'Animal' ; imprimir "El perro es un animal.\n" si $perro -> isa ( 'Animal' );
Se puede lograr una introspección mucho más poderosa en Perl utilizando el sistema de objetos Moose [3] y el protocolo Class::MOP
de metaobjetos ; [4] por ejemplo, puede verificar si un objeto dado cumple una función X :
si ( $object -> meta -> does_role ( "X" )) { # hacer algo ... }
Así es como puedes enumerar los nombres completos de todos los métodos que se pueden invocar en el objeto, junto con las clases en las que se definieron:
para mi $metodo ( $objeto -> meta -> obtener_todos_los_metodos ) { imprimir $metodo -> nombre_completo_calificado , "\n" ; }
El método más común de introspección en Python es usar la dir
función para detallar los atributos de un objeto. Por ejemplo:
clase Foo : def __init __ ( self , val ) : self.x = val def bar ( self ) : devuelve self.x
>>> dir ( Foo ( 5 )) ['__clase__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__módulo__', '__nuevo__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'bar', 'x']
Además, las funciones integradas type
y isinstance
se pueden utilizar para determinar qué es un objeto mientras que hasattr
pueden determinar qué hace un objeto . Por ejemplo:
>>> a = Foo ( 10 ) >>> b = Bar ( 11 ) >>> tipo ( a ) <tipo 'Foo'> >>> esinstancia ( a , Foo ) Verdadero >>> esinstancia ( a , tipo ( a )) Verdadero >>> esinstancia ( a , tipo ( b )) Falso >>> hasattr ( a , 'bar' ) Verdadero
La introspección de tipos es una característica fundamental de Ruby . En Ruby, la clase Object (antepasada de cada clase) proporciona Object#instance_of?
métodos Object#kind_of?
para comprobar la clase de la instancia. Este último devuelve verdadero cuando la instancia particular a la que se envió el mensaje es una instancia de un descendiente de la clase en cuestión. Por ejemplo, considere el siguiente código de ejemplo (puede probarlo inmediatamente con Interactive Ruby Shell ):
$ irb irb(principal):001:0> A = Clase . nuevo => A irb(principal):002:0> B = Clase . nuevo A => B irb(principal):003:0> a = A . nuevo => #<A:0x2e44b78> irb(principal):004:0> b = B . nuevo => #<B:0x2e431b0> irb(principal):005:0> a . instancia_de? A => verdadero irb(principal):006:0> b . instancia_de? A => falso irb(principal):007:0> b . tipo_de? A => verdadero
En el ejemplo anterior, la Class
clase se utiliza como cualquier otra clase en Ruby. Se crean dos clases A
y B
, la primera es una superclase de la segunda, luego se comprueba una instancia de cada clase. La última expresión da como resultado verdadero porque A
es una superclase de la clase de b
.
Además, puedes pedir directamente la clase de cualquier objeto y "compararlos" (el código a continuación supone haber ejecutado el código anterior):
irb(principal):008:0> A . instancia_de? Clase => verdadero irb(principal):009:0> a . clase => A irb(principal):010:0> a . clase . clase => Clase irb(principal):011:0> A > B => verdadero irb(principal):012:0> B <= A => verdadero
En ActionScript (as3), la función flash.utils.getQualifiedClassName
se puede utilizar para recuperar el nombre de clase/tipo de un objeto arbitrario.
// todas las clases utilizadas en as3 deben importarse explícitamente import flash . utils . getQualifiedClassName ; import flash . display . Sprite ; // trace es como System.out.println en Java o echo en PHP trace ( flash . utils . getQualifiedClassName ( "Soy una cadena" )); // "Cadena" trace ( flash . utils . getQualifiedClassName ( 1 )); // "int", vea la conversión dinámica para saber por qué no Number trace ( flash . utils . getQualifiedClassName ( new flash . display . Sprite ())); // "flash.display.Sprite"
Alternativamente, el operador is
se puede utilizar para determinar si un objeto es de un tipo específico:
// trace es como System.out.println en Java o echo en PHP trace ( "Soy una cadena" es una cadena ); // verdadero trace ( 1 es una cadena ); // falso trace ( "Soy una cadena" es un número ); // falso trace ( 1 es un número ); // verdadero
Esta segunda función también se puede utilizar para probar la herencia de clases de los padres:
importar flash . display . DisplayObject ; importar flash . display . Sprite ; // extiende DisplayObject trace ( new flash . display . Sprite () es flash . display . Sprite ); // verdadero trace ( new flash . display . Sprite () es flash . display . DisplayObject ); // verdadero, porque Sprite extiende DisplayObject trace ( new flash . display . Sprite () es String ); // falso
Al igual que Perl, ActionScript puede ir más allá de obtener el nombre de la clase, sino también todos los metadatos, funciones y otros elementos que componen un objeto utilizando la flash.utils.describeType
función; esto se utiliza al implementar la reflexión en ActionScript.
importar flash . utils . describeType ; importar flash . utils . getDefinitionByName ; importar flash . utils . getQualifiedClassName ; importar flash . display . Sprite ; var className : String = getQualifiedClassName ( new flash.display.Sprite ( ) ); // "flash.display.Sprite" var classRef : Class = getDefinitionByName ( className ); // Referencia de clase a flash.display{{No es un error tipográfico|.}}Sprite // p . ej . , 'new classRef()' igual que 'new flash.display.Sprite()' trace ( describeType ( classRef )); // devuelve un objeto XML que describe el tipo // igual que: trace(describeType(flash.display.Sprite));