En informática , la introspección de tipos es la capacidad de un programa para 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 para 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 ).
Ejemplos
C++
C++ admite la introspección de tipos mediante las palabras clave `typeid` y `dynamic cast` de la información de tipos en tiempo de ejecución (RTTI) . La expresión se puede utilizar para determinar si un objeto en particular pertenece a una clase derivada específica. Por ejemplo:dynamic_cast
Persona * p = dynamic_cast < Persona *> ( obj ); si ( p ) { p -> caminar (); }El typeidoperador recupera un std::type_infoobjeto que describe el tipo más derivado de un objeto:
if ( typeid ( Person ) == typeid ( * obj )) { serializePerson ( obj ); }DO#
En C#, la introspección se puede realizar utilizando la ispalabra clave. Por ejemplo:
Si ( obj es Persona ) { // Haz lo que quieras }Objetivo-C
En Objective-C , por ejemplo, tanto el objeto genérico Object como NSObject (en Cocoa / OpenStep ) proporcionan un métodoisMemberOfClass: que devuelve true si el argumento es una instancia de la clase especificada. isKindOfClass:De forma análoga, el método devuelve true si el argumento hereda de dicha clase.
Por ejemplo, supongamos que tenemos una clase Appley una Orangeclase que hereda de Fruit.
Ahora, en el eatmétodo podemos escribir
- ( void ) comer: ( id ) sth { if ([ sth isKindOfClass : [ Fruit class ]]) { // En realidad estamos comiendo una fruta, así que continuamos if ([ sth isMemberOfClass : [ Apple class ]]) { comerManzana ( sth ); } else if ([ sth isMemberOfClass : [ Orange class ]]) { comerNaranja ( sth ); } else { error (); } } else { error (); } }Ahora, cuando eatse llama con un objeto genérico (un id), la función se comportará correctamente dependiendo del tipo de objeto genérico.
Objeto Pascal
La introspección de tipos ha sido parte de Object Pascal desde el lanzamiento original de Delphi, que utiliza RTTI de forma intensiva para el diseño de formularios visuales. En Object Pascal, todas las clases descienden de la clase base TObject, que implementa la funcionalidad básica de RTTI. El nombre de cada clase puede referenciarse en el código para fines de RTTI; el identificador del nombre de la clase se implementa como un puntero a los metadatos de la clase, que pueden declararse y usarse 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 tipo con verificación de tipo, 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+ (una pragma), normalmente TPersistent, y solo para símbolos definidos en la sección publicada. Delphi 2010 amplió esto a casi todos los símbolos.
procedure Form1 . MyButtonOnClick ( Sender : TObject ) ; var aButton : TButton ; SenderClass : TClass ; begin SenderClass := Sender . ClassType ; //devuelve el puntero de la clase de Sender if sender is TButton then begin aButton := sender as TButton ; EditBox . Text := aButton . Caption ; //Propiedad que tiene el botón pero los objetos genéricos no terminan else begin EditBox . Text := Sender . ClassName ; //devuelve el nombre de la clase de Sender como una cadena end ; end ;Java
El ejemplo más sencillo de introspección de tipos en Java es el operador instanceof[ 1 ] . Este instanceofoperador determina si un objeto en particular pertenece a una clase específica (o a una subclase de esa clase, o a una clase que implementa esa interfaz). Por ejemplo:
if ( obj instanceof Person p ) { p . walk (); }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 en particular ), Object.getClass()se Class.getName()puede utilizar:
System.out.println ( obj.getClass ( ) . getName ( ) ) ;PHP
En PHP, la introspección se puede realizar utilizando instanceofel operador. Por ejemplo:
if ( $obj instanceof Person ) { // Haz lo que quieras }Perl
La introspección se puede lograr utilizando las funciones refy en Perl .isa
Podemos realizar una introspección de las siguientes clases y sus instancias correspondientes:
paquete Animal ; sub nuevo { mi $class = shift ; return bendice {}, $class ; }paquete Perro ; usar base 'Animal' ;paquete principal ; mi $animal = Animal -> nuevo (); mi $perro = Perro -> nuevo ();usando:
print "Este es un animal.\n" if ref $animal eq 'Animal' ; print "El perro es un animal.\n" if $dog -> isa ( 'Animal' );Protocolo de metaobjetos
Se puede lograr una introspección mucho más potente en Perl utilizando el sistema de objetos Moose [ 3 ] y el protocolo Class::MOPde meta-objetos ; [ 4 ] por ejemplo, se puede comprobar si un objeto dado realiza un rol X :
if ( $object -> meta -> does_role ( "X" )) { # hacer algo ... }Así es como puedes listar 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 $method ( $object -> meta -> get_all_methods ) { print $method -> fully_qualified_name , "\n" ; }Pitón
El método más común de introspección en Python consiste en utilizar la dirfunción para detallar los atributos de un objeto. Por ejemplo:
clase Foo : def __init __ ( self , val : int ) - > None : self.x = valdef get_x ( self ) -> int : return self . xprint ( dir ( Foo ( 5 ))) # imprime ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__firstlineno__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__static_attributes__', '__str__', '__subclasshook__', '__weakref__', 'get_x', 'x']Además, las funciones integradas typey isinstancese pueden usar para determinar qué es un objeto, mientras que pueden determinar qué hacehasattr un objeto . Por ejemplo:
a : Foo = Foo ( 10 ) b : Bar = Bar ( 11 ) print ( type ( a )) # imprime <type 'Foo'> isinstance ( a , Foo ) # imprime True print ( isinstance ( a , type ( a ))) # imprime True print ( isinstance ( a , type ( b ))) # imprime False print ( hasattr ( a , 'bar' )) # imprime TrueAdemás, se puede acceder a las sugerencias de tipo de Python en tiempo de ejecución y pueden ser utilizadas por bibliotecas como Pydantic para la serialización y validación, y FastAPI para la inyección de dependencias y la generación de esquemas de API.
Rubí
La introspección de tipos es una característica fundamental de Ruby . En Ruby, la clase Object (antepasada de todas las clases) proporciona Object#instance_of?métodos Object#kind_of?para comprobar la clase de la instancia. Este último devuelve verdadero cuando la instancia 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 (puede probarlo inmediatamente con la consola interactiva de Ruby ):
$ irb irb(main):001:0> A = Class . new => A irb(main):002:0> B = Class . new A => B irb(main):003:0> a = A . new => #<A:0x2e44b78> irb(main):004:0> b = B . new => #<B:0x2e431b0> irb(main):005:0> a . instance_of? A => true irb(main):006:0> b . instance_of? A => false irb(main):007:0> b . kind_of? A => trueEn el ejemplo anterior, la Classclase se utiliza como cualquier otra clase en Ruby. Se crean dos clases, Ay B, siendo la primera una superclase de la segunda, y luego se comprueba una instancia de cada clase. La última expresión da verdadero porque Aes una superclase de la clase de b.
Además, puedes solicitar directamente la clase de cualquier objeto y "compararlas" (el código que aparece a continuación presupone que se ha ejecutado el código anterior):
irb(main):008:0> A . instance_of? Class => true irb(main):009:0> a . class => A irb(main):010:0> a . class . class => Class irb(main):011:0> A > B => true irb(main):012:0> B <= A => trueActionScript
En ActionScript (as3), esta función flash.utils.getQualifiedClassNamese puede utilizar para recuperar el nombre de la 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 ( getQualifiedClassName ( "Soy una cadena" )); // "String" trace ( getQualifiedClassName ( 1 )); // "int", ver conversión dinámica para saber por qué no Number trace ( getQualifiedClassName ( new Sprite ())); // "flash.display.Sprite"Alternativamente, el operador ispuede utilizarse 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 ); // verdaderoEsta segunda función también se puede utilizar para probar los padres de la herencia de clases :
import flash.display.DisplayObject ; import flash.display.Sprite ; // extiende DisplayObjecttraza ( new Sprite () es Sprite ); // verdadero traza ( new Sprite () es DisplayObject ); // verdadero, porque Sprite extiende DisplayObject traza ( new Sprite () es String ); // falsoIntrospección metatipo
Al igual que Perl, ActionScript puede ir más allá de obtener el nombre de la clase, pudiendo acceder a todos los metadatos, funciones y otros elementos que componen un objeto mediante la flash.utils.describeTypefunción; esto se utiliza al implementar la reflexión en ActionScript.
import flash . utils . describeType ; import flash . utils . getDefinitionByName ; import flash . utils . getQualifiedClassName ; import flash . display . Sprite ;var className : String = getQualifiedClassName ( new 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()' es lo mismo que 'new flash.display.Sprite()' trace ( describeType ( classRef )); // devuelve un objeto XML que describe el tipo // lo mismo que : trace(describeType(flash.display.Sprite));Véase también
Referencias
Enlaces externos
- Introspección sobre el código Rosetta
- Programación orientada a objetos
- Comparación de lenguajes de programación